紅黑樹及其插入與刪除操作[未完待續]
紅黑樹的定義
紅黑樹是一種自平衡二叉查找樹。它相比於平衡二叉樹的優點在於,其的特性可以讓其在任何條件下保持樹的高度小於等於log n,所以其即使在最壞條件下對於增刪查改這樣的基本操作也能保持O(log n)的時間復雜度;另外,相比於嚴格平衡的AVL樹,紅黑樹是一種不嚴格的平衡二叉樹,這樣就減少了在實際使用過程中為了保持樹的平衡性而在增刪節點時不斷進行的平衡化操作,效率更高。這些特性使得它得到了相比平衡二叉樹更加廣泛的應用,例如C++的STL中的map、set以及Java中的TreeMap都是利用它實現的
紅黑樹有五條性質:
-
每一個節點要么是紅色,要么是黑色
-
根節點是黑色的
-
每個終節點是黑色的
注意,這里的終節點和我們平常理解的葉子節點是不一樣的。《Algorithms》中用sentinel node來表示,它指的是我們所說的葉子節點的子節點,其parent、left、right屬性值都是null。很顯然,它也是需要實例化的,為了節省不必要的開銷,書中將所有的終節點指針都指向同一個對象,當然這樣就沒有辦法用該節點的parent屬性了
圖片1
-
如果一個節點是紅色的,則它的兩個孩子節點一定是黑色的
-
對於每一個節點,從該節點到終節點的每一條路徑上有相同數量的黑色節點
證明:有n個非終節點的紅黑樹的高度最高為2log(n+1)
引理:以節點x為根的子樹包括至少2^(bh(x))-1個非終節點。其中bh(x)叫黑色高度,指的是從這個點(不包括該點)到終結點(包括終結點)的黑色點個數
證明引理:使用數學歸納法
-
如果子樹x的高度為0,則也就是說這個子樹只有x這一個節點,所以內部有2^(0)-1=0個非終節點。
-
如果子樹高度不為0。由於其為黑色節點(為什么一定是黑色節點?因為紅黑樹的根必須是黑色的),其子節點可以說紅色,也可以是黑色,所以對於其兩個子節點來說,黑色高度可能是bh(x)[子節點為紅色的情況],也可能是bh(x)-1[子節點為黑色的情況,也是能構成子樹的情況,下面討論的就是這種]。如果假設該引理正確,則對於其子節點來說,由其為根的子樹至少有2(bh(x)-1)-1個非終節點,那么對於節點x,其非終結點數量就是2*2(bh(x)-1)-2+1=2^(bh(x))-1。(注意最后加的那個1是節點x自己)
-
引理得證
證明原命題
由於性質4,從根節點到終結點的每一條路徑上,至少有一半的節點是黑色的(因為出現紅色節點必須跟着一對黑色子節點,而黑色節點則是沒有要求的),所以我們假設一棵樹高度為h,那么這樣的一條支路上至少有h/2個黑色節點,也就是說這棵樹的黑色高度至少為h/2,由引理可知,該樹至少有2^(h/2)-1個非終節點,方程化簡一下就能得到命題結果。
由這個命題我們可以看出來,節點的高度一定是log級別的,那么由於其是有序的,在增刪查改時采用的是二叉搜索樹通用的方法,所以遍歷的高度不會高於樹的高度,這就是紅黑樹能保持O(logn)的原因。
紅黑樹的插入
拖更了~
紅黑樹的刪除
紅黑樹的刪除由於情況之多(有六種不同情況),所以是它最難的地方,這里我就自己掌握的情況嘗試進行講解,許多資料當然還是參考其他博主和書籍的。
還記得我們在AVL樹中刪除節點時的操作嗎?對於有兩個子節點的節點,我們不能直接刪除,而是需要
- 找到該節點的前驅或者后繼
- 將前驅或者后繼的值賦給該節點
- 刪除該前驅或者后繼
由於前驅或者后繼一定是葉子節點或者有一個子節點的節點,所以最終的刪除問題可以歸結為兩類:
- 刪除的節點是葉子節點
- 刪除的節點只有一個子節點
我們要搞清楚一個問題,對於紅黑樹,我們關注的不是節點是怎樣被刪掉的,而是關注在刪除節點時如何保證紅黑樹的性質不被破壞。這里的邏輯是:
- 確定要刪除的節點
- 對樹進行變換(其實涉及到的范圍主要還是在要刪除的節點附件)
- 刪除該節點
第三部分很簡單,我們先來看一下:如果要刪除的節點是葉子節點,直接刪除就好;如果有一個子節點,則用它來代替該節點原來的位置。
好了,第三部分回答完了,現在我們主要來看第二部分。
我們要刪除的節點要么是紅色的,要么是黑色的。如果是紅色的,就沒有什么影響,第2步是不需要的,直接刪除就好了。關鍵我們要分析如果它是黑色的應該怎么辦。
先上一張來自博主(https://www.cnblogs.com/nullllun/p/8214599.html)的圖

根據圖片我們可以看到,這里把刪除時候的情況分為了六種,為了之后敘述的簡單,我們做一些符號上的約定:
- N表示待刪除節點
- P表示N的雙親節點
- S表示兄弟節點,即P的另外一個子節點
- SL表示S的左子節點
- SR表示S的右子節點
現在我們來看六種情況:
- 樹只有一個節點(當然就是N了)
- N、P、SL、SR為黑色,S為紅色
- N、S、SL、SR為黑色,P為紅色
- N、S、SR為黑色,P哪種顏色均可,SL為紅色
- N、S為黑色,SL、P哪種顏色均可,SR為黑色
- N、S、P、SL、SR均為黑色
注意,我這里的分類沒有按照圖片中的順序來,是為了盡可能顯示分類的完備性,之后講解還是會按圖中順序來的。
分析分類的完備性
-
沒有紅色節點:這種情況包括了
-
有一個紅色節點:這種情況包括了
-
有兩個紅色節點:根據紅黑樹的性質,能出現同時兩個為紅色節點的情況只有:
- P\SL
- P\SR
- SL\SR
這三種情況也包括了
-
有三個紅色節點:只有P\SL\SR這一種,也包括了
-
不可能有四個及以上的紅色節點
也就是說,對於每一種可能的情況,我們都可以給出要達到紅黑樹平衡條件應該進行的變換,只要我們分析得知每一種情況的變換都是正確的,那刪除的問題就解決了!剩下的就是編程問題了
下面正式進行分析,具體的操作請見圖片
對於情況一不解釋,只有一個節點,刪了就刪了
情況二:原來1-6節點從子樹根節點開始經過的黑色節點數為2,2,2,2,2,2。對R節點進行左旋並交換P和S的顏色。問題轉換到了P為紅色,其子節點N和SL為黑色的問題,即情況四,此時按情況四的做法交換P和SL的顏色,變換之后1-6經過的黑色節點數為2,2,2,2,2,2,所以沒有變化。
情況三:將S變成紅色即可,刪除N后兩叉的黑色高度與一開始依然保持了一致
情況四:交換P和S的顏色,變成和情況三一樣的狀態
情況五:將S節點右旋並交換SL和S的顏色,此時問題轉換為了情況六,操作方法看情況六
情況六:將P節點左旋並與S交換顏色,再將SR變為黑色。假設P和SL是紅色(是紅是黑都無妨,這里只是選了一種情況來計數),原來1,2,3,4,5,6節點從子樹根節點開始經過的黑色節點數為1,1,1,1,1,1。變換之后經過的黑色節點數依然是1,1,1,1,1,1(注意N是被刪了的,1或者2在刪除后是直接和P相聯接的),所以沒有變化.
這里很重要的一點是我們考慮的應該是刪除對整個樹的影響,這里分析的1-6在片段中經過的黑色個數,可以理解為這一段樹和其余段之間的”接口“就是1-6,這一段樹下面的節點的黑色高度當然不會變,而我們證明了上面的節點黑色高度也不變,就能證明它沒有影響到各個支路上黑色節點的總數量,也就是從”最上面“的根節點通過各個支路到終結點的黑色節點數量。這個可以好好理解下,很容易理解的。
程序實現
拖更了~
