红黑树,看不懂你找我
文章目錄
- 一:什么是紅黑樹
- 二:關于紅黑樹的一般操作
- 1.查找操作
- 2. 插入操作
- 2.1 新節點:我是紅色的,我爹是紅色的,我叔叔是黑色的
- 2.2 新節點:我是紅色的,我爹是紅色的,我叔叔也是紅色的
- 3.刪除操作
- 3.1替代點是紅色的
- 3.2替代點是黑色的
- 3.2.1 有一個兒子為且必須為紅色時
- 3.2.2 有兩個黑色兒子
- 3.2.2.1 替代點的兄弟節點是紅色
- 3.2.2.2 替代點的兄弟節點是黑色
- 3.2.2.2.1 當兄弟節點的兩個兒子也都是黑色
- 3.2.2.2.2 當兄弟節點的左兒子是紅色的,右兒子是黑色
- 3.2.2.2.3 當兄弟節點的右兒子是紅色的,左兒子隨意
- 三:漸進邊界的證明
一:什么是紅黑樹
紅黑樹是AVL樹的一種流行變種,是具有以下五個特點的二叉查找樹:
二:關于紅黑樹的一般操作
對紅黑樹操作,最困難的就是保持上述的紅黑樹性質中的紅色標記部分。
1.查找操作
紅黑樹是二叉查找樹,所以其查找操作與一般的二叉查找樹相同
- 令根節點為當前節點
- 當前節點為Nil,返回Nil
- 要找的值等于當前節點,返回當前節點
- 要找的值大于當前節點, 令右兒子為當前節點,重復步驟2
- 要找的值小于當前節點,令左兒子為當前節點,重復步驟2
操作的時間復雜度O(logN)
2. 插入操作
當我們想向樹中插入一個新節點時,通常把這個節點作為葉節點插入。
Part1:
假設把這個點涂成黑色,那完了,肯定違反了條件5,因為本來任意的節點到某Nil指針的路徑中黑色節點數是相同的,現在加上黑色節點的這條路徑黑色節點數度肯定會+1,于是原來的條件被破壞了,因此,新加的節點必須被涂成紅色。
Part2:
如果新插入的節點的父節點是黑色的,那么直接插入新的紅節點就完事了
Part3:
如果新插入的節點的父節點是紅色,那么條件4就被破壞了,這種情況下,我們必須在保持條件5不被破壞的同時,利用改變節點顏色以及樹的旋轉來滿足所有條件。
2.1 新節點:我是紅色的,我爹是紅色的,我叔叔是黑色的
- 設置P為黑色
- 設置G為紅色
- 進行右單旋轉
- 設置X為黑色
- 設置G為紅色
- 進行右雙旋轉
還有對稱的兩種情況,操作也是對稱的
2.2 新節點:我是紅色的,我爹是紅色的,我叔叔也是紅色的
- 把G節點設置為紅色
- 把P節點設置為黑色
- 把S節點設置為黑色
- 把G節點設置為紅色
- 把P節點設置為黑色
- 把S節點設置為黑色
這時候,如果G的父節點也是紅的,那么G的顏色翻轉的操作就會影響性質4,于是我們對G節點以及其父節點GP和父節點的兄弟節點GPS進行2.1所示的旋轉操作。
注意如果G的父節點GP為紅色,G父節點的兄弟節點GPS肯定不能為紅色,只能為黑色,因為在紅黑樹中,不可能出現出現深度小于樹的總深度且雙兄弟都為紅的情況,這種情況在其他的插入或刪除過程中已經消除了。
GP剛好為根結點時,那么根據性質2,我們必須把GP重新設為黑色,那么樹的紅黑結構變為:黑黑紅。換句話說,從根結點到葉子結點的路徑中,黑色結點增加了。這也是唯一一種會增加紅黑樹黑色結點層數的插入情景。
還有對稱的兩種情況,操作也是對稱的
偽代碼
RB-INSERT(T, z)//紅黑樹插入y = T.nilx = T.rootwhile x != T.nily = xif z.key<x.keyx = x.leftelse x = x.rightz.p = yif y == T.nilT.root = zelse if z.key < y.keyy.left = zelse y.right = zz.left = T.nilz.right = T.nilz.color = RED//對紅黑樹的性質進行恢復RB-INSERT-FIXUP(T, z)RB-INSERT-FIXUP(T, z)//插入紅色子節點,破壞了性質4時while z.p.color = RED//父節點為左子樹if z.p == z.p.p.left//獲取叔叔結點yy = z.p.p.right//叔叔結點為紅色if y.color = REDz.p.color = BLACKy.color = BLACKz.p.p.color = REDz = z.p.p//叔叔結點為黑色else //新插入的結點為父節點的右孩子,先來個父子調換,先進行一次左旋轉if z == z.p.rightz = z.pLEFT-ROTATE(T,z)//化成了叔叔結點黑色的一般情況z.p.color = BLACKz.p.p.color = REDRIGHT-ROTATE(T, z.p.p)//對稱情況else (same as then clause with "right" and "left" exchanged)T.root.color = BLACK難點來了,難點來了!!
其實不難
3.刪除操作
刪除操作一直一來是樹這種ADT的難點所在
一般二叉搜索樹的刪除操作:
- 該節點為葉節點的情況:可以直接刪除,將其父節點的兒子指針設為Nil
- 該節點只有一個非空子節點:可以直接刪除,將其父節點的兒子指針指向其唯一兒子
- 該節點有兩個非空子節點:用該節點左子樹的最大節點或者右子樹的最小節點替換該節點,然后刪除對應的左子樹最大節點或者右子樹最小節點,最終可以回到前兩種情況。
好,到這里你肯定還是明白的對吧
現在考慮紅黑樹的刪除操作。
注意:
根據一般二叉搜索樹的刪除操作來看,我們要刪除的節點其實不一定是最終從圖中移出去的節點,稱最終從圖中移出去的節點為替代點,我們先從右子樹的最小節點(或左子樹的最大節點)找到我們的替代點,與刪除點的值交換,但是顏色都不變,然后再刪除替代點,然后對樹的性質進行恢復。而現在這些替代點都是一些樹的末節點(區別于葉節點,我們現在將Nil節點視為黑色葉節點),刪除操作便會簡化許多,并可以歸納為像插入那樣的幾個類別。當然了替代點和我們要刪除的點也有可能是同一點,但處理方法是一樣的。
我們雖然刪除了替代點,但是我們對樹進行恢復時,還是會將該替代點放在樹中參與恢復,恢復完成后再移掉。
在本文中,示例都默認是選擇右子樹的最小節點作為真正刪除點,也可以選擇左子樹的最大節點
3.1替代點是紅色的
直接從樹中去掉即可,不會影響紅黑樹的性質,算法直接結束
不會出現替代點為紅色且有兒子為非葉子節點的情況,所以這種情況是最簡單的
3.2替代點是黑色的
替代點是黑色,那么刪除之后樹的性質就被破壞了,需要進行修復
3.2.1 有一個兒子為且必須為紅色時
當替代節點只有一個兒子且為紅色時
直接用它的紅色兒子節點取代它,并將顏色改為黑色,這樣所有經過替代節點的路徑都將經過他的兒子,黑色高度不變。
注意這跟上面圖不一樣,這張圖D節點是替代節點
不可能存在只有一個兒子且兒子為黑色的情況,那樣本身就是不平衡的
3.2.2 有兩個黑色兒子
當替代點是黑色的且有兩個黑色兒子(可以為葉節點Nil)時,那他肯定有兄弟,那么又可以分為其他幾類
- 替代點是黑色,其兄弟節點是紅色
- 替代點是黑色,其兄弟節點是黑色
3.2.2.1 替代點的兄弟節點是紅色
- 將G節點也就是替代點的父節點設置為紅色,
- 兄弟節點S設置為黑色
- 然后進行向左單旋轉,
現在可以直接刪掉D了嗎?(以下都默認標識D為替代點) 不行,tan90°\hspace4ex 不行,tan90\degree不行,tan90°
這個操作不改變任何路徑的黑色高度,這時候刪去D肯定會變的不平衡,我們只是把情況變為了兄弟節點為黑色的情況,也就是下面將要敘述的情況,我們接下來需要重新進入算法,進一步處理
3.2.2.2 替代點的兄弟節點是黑色
替代點D與其兄弟節點S都是黑色的,所以D的父節點顏色是不確定的,用綠色表示
又分為以下幾種情況
3.2.2.2.1 當兄弟節點的兩個兒子也都是黑色
PS:C1、C2可為Nil
- S節點顏色變為紅
- G節點顏色變黑
- 把G作為新的替代點進行下一輪操作
假設G一開始是黑色的,這個操作過后,所有一開始經過S節點的路徑黑色高度-1,那么在刪除D節點后,所有經過D節點的路徑黑色高度也會-1,這棵子樹大家黑色高度都減一,結局竟該死的甜美,但是經過G的比不經過G的黑色高度減一了,所以再在G上重新平衡處理
假設G一開始是紅色的,這個操作后,所有一開始經過S節點的路徑黑色高度不變,經過D的路徑黑色高度+1,那么在刪除D后,所有經過D節點的路徑黑色高度又變回來了,結局竟還是該死的甜美,經過G為根的子樹已經平衡了,再在G上重新平衡處理
注意當G是樹的根時,就表示我們已經做完了,我們從所有路徑去除了一個黑色節點,所有性質在上溯過程中都保持著。
3.2.2.2.2 當兄弟節點的左兒子是紅色的,右兒子是黑色
- C1設置為黑色
- S設置為紅色
- 對S進行向右單旋轉
這樣的操作不改變任何路徑的黑色高度,本來的平衡狀態不變,所以刪除D之后就破壞了原先的條件,還要進行自平衡變換,此時成了下面一種情況繼續處理
3.2.2.2.3 當兄弟節點的右兒子是紅色的,左兒子隨意
- 交換G和S的顏色
- 設置兄弟節點的右兒子C2為黑色
- 進行向左單旋轉
該操作使所有變化前經過S節點的路徑黑色高度都不變,經過D的路徑黑色高度+1,在刪除D后,所有經過G節點的路徑都不變,達到平衡
說白了刪除就是不斷遞歸變換直到滿足替代點的刪除條件。
例子:刪除30根節點
練習一下吧,這里有個在線紅黑樹工具,戳
三:漸進邊界的證明
包含n個內部節點的紅黑樹的高度是O(logn)O(logn)O(logn)
定義:
h(v)表示以節點v為根的子樹的高度。{\displaystyle h(v)}表示以節點{\displaystyle v}為根的子樹的高度。h(v)表示以節點v為根的子樹的高度。
bh(v)表示從v到子樹中任何葉子的黑色節點的數目{\displaystyle bh(v)}表示從{\displaystyle v}到子樹中任何葉子的黑色節點的數目bh(v)表示從v到子樹中任何葉子的黑色節點的數目
(如果v是黑色則不計數它,也叫做黑色高度)。(如果{\displaystyle v}是黑色則不計數它,也叫做黑色高度)。(如果v是黑色則不計數它,也叫做黑色高度)。
引理:以節點v為根的子樹有至少2bh(v)?1個內部節點。引理:以節點{\displaystyle v}為根的子樹有至少2^{bh(v)}-1個內部節點。引理:以節點v為根的子樹有至少2bh(v)?1個內部節點。
引理的證明(通過歸納高度):引理的證明(通過歸納高度):引理的證明(通過歸納高度):
歸納假設:歸納假設:歸納假設:
如果v的高度是零則它必定是NIL,因此bh(v)=0bh(v)=0。所以:2bh(v)?1=20?1=0如果v的高度是零則它必定是NIL,因此{\displaystyle bh(v)=0}{\displaystyle bh(v)=0}。所以:2^{{bh(v)}}-1=2^{{0}}-1=0如果v的高度是零則它必定是NIL,因此bh(v)=0bh(v)=0。所以:2bh(v)?1=20?1=0
如果h(v)=k,有2bh(v)?1個內部節點成立如果h(v)=k,有2^{bh(v)}-1個內部節點成立如果h(v)=k,有2bh(v)?1個內部節點成立
于是h(v′)=k+1于是h(v')=k+1于是h(v′)=k+1
因為v′有h(v′)>0所以它是個內部節點。同樣的它有的兩個兒子,其黑色高度要么是bh(v′)要么是bh(v′)?1(依據v′是紅色還是黑色)。通過歸納假設每個兒子都有至少2bh(v′)?1?1個內部接點,所以v′有:因為v'有h(v')>0 \\所以它是個內部節點。同樣的它有的兩個兒子,其黑色高度要么是bh(v')要么是bh(v')-1(依據v'是紅色還是黑色)。通過歸納假設每個兒子都有至少2^{{bh(v')-1}}-1個內部接點,所以v'有:因為v′有h(v′)>0所以它是個內部節點。同樣的它有的兩個兒子,其黑色高度要么是bh(v′)要么是bh(v′)?1(依據v′是紅色還是黑色)。通過歸納假設每個兒子都有至少2bh(v′)?1?1個內部接點,所以v′有:
2bh(v′)?1?1+2bh(v′)?1?1+1=2bh(v′)?1個內部節點。2^{bh(v')-1}-1+2^{bh(v')-1}-1+1=2^{bh(v')}-1個內部節點。2bh(v′)?1?1+2bh(v′)?1?1+1=2bh(v′)?1個內部節點。
使用這個引理我們現在可以展示出樹的高度是對數性的。因為在從根到葉子的任何路徑上至少有一半的節點是黑色(根據紅黑樹性質4),根的黑色高度至少是h(root)2通過引理我們得到:使用這個引理我們現在可以展示出樹的高度是對數性的。\\ 因為在從根到葉子的任何路徑上至少有一半的節點是黑色(根據紅黑樹性質4),\\根的黑色高度至少是 {\frac {h(root)}{2}}通過引理我們得到:使用這個引理我們現在可以展示出樹的高度是對數性的。因為在從根到葉子的任何路徑上至少有一半的節點是黑色(根據紅黑樹性質4),根的黑色高度至少是2h(root)?通過引理我們得到:
n?2h(root)2?1?log?(n+1)?h(root)2?h(root)?2log?(n+1)n?2h(root)2?1?log?(n+1)?h(root)2?h(root)?2log?(n+1){\displaystyle n\geqslant 2^{\frac {h(root)}{2}}-1\leftrightarrow \;\log {(n+1)}\geqslant {\frac {h(root)}{2}}\leftrightarrow \;h(root)\leqslant 2\log {(n+1)}}n\geqslant 2^{{{\frac {h(root)}{2}}}}-1\leftrightarrow \;\log {(n+1)}\geqslant {\frac {h(root)}{2}}\leftrightarrow \;h(root)\leqslant 2\log {(n+1)}n?22h(root)??1?log(n+1)?2h(root)??h(root)?2log(n+1)n?22h(root)??1?log(n+1)?2h(root)??h(root)?2log(n+1)
因此根的高度是O(log?n)因此根的高度是{\displaystyle {\text{O}}(\log n)}因此根的高度是O(logn)
總結
以上是生活随笔為你收集整理的红黑树,看不懂你找我的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 你了解欧拉回路吗?(附Java实现代码)
- 下一篇: 从JVM看类的加载过程与对象实例化过程