資料結構之-深入理解紅黑樹
概述
本文將會透徹理解什麼是紅黑樹,有什麼特點、優點與缺點,與其它樹結構(二叉查詢樹、平衡二叉樹、2-3-4樹)有什麼區別和聯絡。寫作本文的目的旨在加深自己的理解,文中許多內容參考了網路上的文章並根據自己的理解進行了整理。
第一部分:什麼是紅黑樹
紅黑樹(英語:Red–black tree),一種二叉查詢樹,但在每個結點上增加一個儲存位表示結點的顏色,可以是Red或Black。紅黑樹,作為一棵二叉查詢樹,滿足二叉查詢樹的一般性質。下面,來了解下 二叉查詢樹的一般性質。
二叉查詢樹,也稱有序二叉樹(ordered binary tree),或已排序二叉樹(sorted binary tree),是指一棵空樹或者具有下列性質的二叉樹:
- 若任意節點的左子樹不空,則左子樹上所有結點的值均小於它的根結點的值;
- 若任意節點的右子樹不空,則右子樹上所有結點的值均大於它的根結點的值;
- 任意節點的左、右子樹也分別為二叉查詢樹。
- 沒有鍵值相等的節點(no duplicate nodes)。
紅黑樹通過對任何一條從根到葉子的路徑上各個結點著色方式的限制,紅黑樹確保沒有一條路徑會比其他路徑長出倆倍,因而是接近平衡的。因此紅黑樹也是B樹(Balance Tree)的一種,紅黑樹相對於B樹來說,犧牲了部分平衡性以換取插入/刪除操作時少量的旋轉操作,整體來說效能要優於B樹(不容易不平衡,減少了再平衡操作)。下面是平衡二叉樹的性質:
平衡二叉樹的左子樹和右子樹都是平衡二叉樹,且左子樹和右子樹的深度之差的絕對值(平衡因子)不超過1。也就是說平衡二叉樹每個節點的平衡因子只可能是-1、0和1。
在二叉查詢樹強制一般要求以外,紅黑樹增加了如下的額外要求:
- 節點是紅色或黑色。
- 根是黑色。
- 葉子節點都是黑色(葉子是NIL節點)。
- 葉子節點不包含資料。
- 每個紅色節點必須有兩個黑色的子節點。(從每個葉子到根的所有路徑上不能有兩個連續的紅色節點。)
- 從任一節點到其每個葉子的所有簡單路徑都包含相同數目的黑色節點。
上述條件使紅黑樹具有如下特點:最深的的葉子節點的深度不會大於兩倍的最淺葉子節點的深度。所以,紅黑樹總是半平衡的。
為什麼會這樣?第六個屬性保證了擁有紅色節點的路徑要比純黑色節點的路徑長。所以最短的路徑 ,是隻包含黑色節點的路徑。第五個屬性保證了沒有相連的紅色節點,所以最長的路徑,是紅黑交替的路徑。
紅黑樹的特點、優點與缺點
- 紅黑樹是二叉查詢樹,所以紅黑樹具有二叉查詢樹的所有特性。
- 紅黑樹是半平衡的,所以紅黑樹是B樹的一種但不嚴格符合B樹的平衡條件,即平衡因子的絕對值不大於1。
- 紅黑是用非嚴格的平衡來換取增刪節點時候旋轉次數的降低,因此在增加或者刪除節點的時候,根據不同情況,B樹旋轉的次數比紅黑樹要多。所以紅黑樹的插入、刪除效率更高!!!
- 由於B樹高度平衡(插入/刪除更容易引起不平衡),因此B樹的search效率更高
第二部分:為什麼說紅黑樹與2-3-4樹是等價的資料結構
先說一下什麼是2-3-4樹?2-3-4樹是四階的 B樹(Balance Tree),但不是二叉樹。它的結構有以下限制:
- 所有葉子節點都擁有相同的深度。
- 節點只能是 2-節點、3-節點、4-節點之一。
2-節點:包含 1 個元素的節點,有 2 個子節點;
3-節點:包含 2 個元素的節點,有 3 個子節點;
4-節點:包含 3 個元素的節點,有 4 個子節點;- 元素始終保持排序順序,整體上保持二叉查詢樹的性質,即父結點大於左子結點,小於右子結點;而且結點有多個元素時,每個元素必須大於它左邊的和它的左子樹中元素。
一棵樹在查詢看來變得不平衡是因為子樹的高度相差很大。二叉樹為什麼會這麼容易變得不平衡,很簡單,因為它只有二叉,左右均有50%的概率,那麼插入N個節點全部都是左節點或者右節點的概率就是50%的N次方,如果是8叉樹,那麼這個概率就是12.5%的N次方。
樹的寬度越大,高度越小,這樣查詢起來越快。 我們也不能一下子就上256叉樹,即使那樣在海量節點情況下也抗不住,因此這種盲目寬度換高度的方案沒有可擴充套件性。我們需要找出一種動態的機制,讓一棵樹動態調整保持平衡。
2-3樹的平衡變換
如果是二叉樹,那麼你插入一個節點,你只有最多1次機會保持子樹的高度不變,如果是一個三叉樹,那麼就有2次機會。現在開始,我們為二叉樹添了一叉,變成了三叉樹。
二叉樹的時候,一個節點有兩個分支,三叉樹的時候,有三個分支。一個點可以將區間分為兩個部分割槽域,要想將一個區間分為三個部分割槽域,就需要兩個點,因此三叉的情形下,節點儲存的是兩個點而不是一個,如右下圖所示:
現在考慮插入一個新節點,這個2-3樹怎麼保持平衡。非常簡單,我們知道,插入的位置一定是葉子,假設當前的樹是平衡的,現在分兩種情況:
1).插入的新葉子節點的父節點是一個二叉節點
這種情況最簡單,二叉節點變三叉節點即可,如下圖所示:
2).插入的新葉子節點的父節點是一個三叉節點
這種情況比較複雜。樹總是要長高的,保持平衡的方式就是同時長高,而這是不可能的,插入一個節點只能讓該節點所在的子樹長高。然而,如果能將這個資訊上升到根部,在根部長高,就實現了“同時長高”!
還是循著上面的那個思路,我們繼續增加樹叉的數量,我們把它增加到4!新節點的插入如下圖所示:
很遺憾,沒有完成任務,但是最終我們提出了兩個問題,只要解決了這兩個問題,所有問題就解決了。解決這兩個問題,無疑都要牽扯到節點P的父節點以及再往上的節點,有兩種可能:
可能性1:P的父節點PP是一個二叉節點
這個太爽,我們直接把P以及它的子樹全部提到PP節點即可,類似B插入的情景,如下圖所示:
問題2解決。
可能性2:P的父節點PP是一個三叉節點
這就有點不好辦了,不管怎樣先把P節點以及其子節點全部上提到PP,保持最底部的平衡性,這樣就可以遞迴解決了,此時我們又一次遇到了往一個三叉節點裡面插入子節點的問題了,為了不增加樹高,唯一的方式就是膨脹成一個四叉節點-寬度換高度。如下圖所示:
最後,我們發現,在遞迴的過程中,要麼碰到了P..P是個二叉節點,此時按照問題2的解決方式將當前節點的值直接提到P...P中,其子樹降低一個高度,抵消增加的高度,平衡保持,遞迴結束,要麼遞迴到了根節點,此時只需要一個分裂操作即可完美結束!
演進到紅黑樹
很顯然,通過上面的描述,我們似乎找到了一個使樹保持平衡的方案,而且是相當完美的平衡!核心就是寬度和高度之間的博弈。我們總是可以用一個寬度抵消一層高度,整個過程就是一次或者多次的一加一減,最終的結果還是0!
然而,這也不再是二叉樹了,有的節點變成了三叉,並且儲存了兩個值,該兩個值將區間分割成了三部分,是為三叉!因此在使用上就不如二叉樹方便,比較操作複雜化了。事實上,將三叉節點處理成二叉節點,這棵樹就成了紅黑樹!怎麼處理呢?很簡單!如下圖所示:
看到了吧,紅色節點就是從2-3樹中分出來的,為了維持一棵二叉樹而不是2-3樹,必須將三叉節點變成二叉節點,這是一個寬度換高度得回退,即高度換寬度,當然代價就是不再完美平衡。
按照以上的這個變換,你自己試試看,可以變出兩個連續的紅節點嗎?NO!下面我們來看一下它的最壞情況是什麼? 還是以2-3樹分析,如果在一棵2-3樹中,最左邊路徑上的節點全部是三叉節點,而最右邊路徑上的節點都是二叉節點,那麼把它變換成二叉紅黑樹之後,就會發現最左邊的路徑上是紅黑間隔的節點,而最右邊的路徑上全部是黑節點,它們的高度差接近2倍。出現這樣的情況是令人悲哀的,但是也是極低概率的。
事實上, 2-3-4樹的每一個結點都對應紅黑樹的一種結構,所以每一棵 2-3-4樹 也都對應一棵紅黑樹,下圖是 2-3-4樹不同結點與紅黑樹子樹的對應。紅黑樹的所有操作包括旋轉等,都可以對映到2-3樹中。
第三部分:操作紅黑樹
參考內容:
連結:https://blog.csdn.net/v_JULY_v/article/details/6105630
連結:https://www.imooc.com/article/22520