為什么工程中都喜歡用紅黑樹,而不是其他平衡二叉查找樹呢?


前言

  二叉查找樹是最常用的一種二叉樹,它支持快速插入、刪除、查找操作,各個操作的時間復雜度跟樹的高度成正比,理想情況下,時間復雜度是O(logn)

  不過,二叉查找樹在頻繁的動態更新過程中,可能會出現樹的高度遠大於log2n 的情況,從而導致各個操作的效率下降。極端情況下,二叉樹會退化為鏈表,時間復雜度會退化到 O(n)。我上一節說了,要解決這個復雜度退化的問題,我們需要設計一種平衡二叉查找樹

1 平衡二叉樹定義

  平衡二叉樹的嚴格定義是這樣的:二叉樹中任意一個節點的左右子樹的高度相差不能大於 1。平衡二叉查找樹不僅滿足定義,還滿足二叉查找樹的特點。最先被發明的平衡二叉查找樹是AVL樹。它嚴格符合平衡二叉查找樹的定義,是一種高度平衡的二叉查找樹。

  但是很多平衡二叉查找樹其實並沒有嚴格符合上面的定義(樹中任意一個節點的左右子樹的高度相差不能大於 1),比如紅黑樹,它從根節點到各個葉子節點的最長路徑,有可能會比最短路徑大一倍。

  發明平衡二叉查找樹這類數據結構的初衷是,解決普通二叉查找樹在頻繁的插入、刪除等動態更新的情況下,出現時間復雜度退化的問題。

  所以,平衡二叉查找樹中“平衡”的意思,其實就是讓整棵樹左右看起來比較“對稱”、比較“平衡”,不要出現左子樹很高、右子樹很矮的情況。這樣就能讓整棵樹的高度相對來說低一些,相應的插入、刪除、查找等操作的效率高一些。

  所以,如果我們現在設計一個新的平衡二叉查找樹,只要樹的高度不比 log2n 大很多(比如樹的高度仍然是對數量級的),盡管它不符合我們前面講的嚴格的平衡二叉查找樹的定義,但我們仍然可以說,這是一個合格的平衡二叉查找樹。

2 如何定義一顆“紅黑樹”

  紅黑樹的英文是“Red-Black Tree”,簡稱 R-B Tree。它是一種不嚴格的平衡二叉查找樹。紅黑樹中的節點,一類被標記為黑色,一類被標記為紅色。除此之外,一棵紅黑樹還需要滿足這樣幾個要求:

  • 根節點是黑色的;
  • 每個葉子節點都是黑色的空節點(NIL),也就是說,葉子節點不存儲數據;
  • 任何相鄰的節點都不能同時為紅色,也就是說,紅色節點是被黑色節點隔開的;
  • 每個節點,從該節點到達其可達葉子節點的所有路徑,都包含相同數目的黑色節點;

      如果要證明紅黑樹是近似平衡的,我們只需要分析,紅黑樹的高度是否比較穩定地趨近 log2n 就好了。

  首先,我們來看,如果我們將紅色節點從紅黑樹中去掉,那單純包含黑色節點的紅黑樹的高度是多少呢?

  紅色節點刪除之后,有些節點就沒有父節點了,它們會直接拿這些節點的祖父節點(父節點的父節點)作為父節點。所以,之前的二叉樹就變成了四叉樹

  前面紅黑樹的定義里有這么一條:從任意節點到可達的葉子節點的每個路徑包含相同數目的黑色節點。我們從四叉樹中取出某些節點,放到葉節點位置,四叉樹就變成了完全二叉樹。所以,僅包含黑色節點的四叉樹的高度,比包含相同節點個數的完全二叉樹的高度還要小。

  完全二叉樹的高度近似 log2n,這里的四叉“黑樹”的高度要低於完全二叉樹,所以去掉紅色節點的“黑樹”的高度也不會超過 log2n。

  現在把紅色節點加回去,高度會變成多少呢?

  從上面我畫的紅黑樹的例子和定義看,在紅黑樹中,紅色節點不能相鄰,也就是說,有一個紅色節點就要至少有一個黑色節點,將它跟其他紅色節點隔開。紅黑樹中包含最多黑色節點的路徑不會超過 log2n,所以加入紅色節點之后,最長路徑不會超過 2log2n,也就是說,紅黑樹的高度近似 2log2n。

  所以,紅黑樹的高度只比高度平衡的 AVL 樹的高度(log2n)僅僅大了一倍,在性能上,下降得並不多。這樣推導出來的結果不夠精確,實際上紅黑樹的性能更好。

解答開題

  我們前面提到 Treap、Splay Tree都是平衡二叉樹,絕大部分情況下,它們操作的效率都很高,但是也無法避免極端情況下時間復雜度的退化。盡管這種情況出現的概率不大,但是對於單次操作時間非常敏感的場景來說,它們並不適用。

  AVL 樹是一種高度平衡的二叉樹,所以查找的效率非常高,但是,有利就有弊,AVL 樹為了維持這種高度的平衡,就要付出更多的代價。每次插入、刪除都要做調整,就比較復雜、耗時。所以,對於有頻繁的插入、刪除操作的數據集合,使用 AVL 樹的代價就有點高了。

  紅黑樹只是做到了近似平衡,並不是嚴格的平衡,所以在維護平衡的成本上,要比 AVL 樹要低。

  所以,紅黑樹的插入、刪除、查找各種操作性能都比較穩定。對於工程應用來說,要面對各種異常情況,為了支撐這種工業級的應用,我們更傾向於這種性能穩定的平衡二叉查找樹。

  因為紅黑樹是一種性能非常穩定的二叉查找樹,所以,在工程中,但凡是用到動態插入、刪除、查找數據的場景,都可以用到它。不過,它實現起來比較復雜,如果自己寫代碼實現,難度會有些高,這個時候,我們其實更傾向用跳表來替代它。

總結

  紅黑樹是一個讓我又愛又恨的數據結構,“愛”是因為它穩定、高效的性能,“恨”是因為實現起來實在太難了。

  學習紅黑樹的代碼實現,對於平時做項目開發沒有太大幫助。對於絕大部分開發工程師來說,這輩子可能都用不着親手寫一個紅黑樹。除此之外,它對於算法面試也幾乎沒什么用,一般情況下,靠譜的面試官也不會讓你手寫紅黑樹的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM