資料結構之紅黑樹
此文是資料結構與演算法之美學習筆記
二叉查詢樹在頻繁的動態更新的過程中,可能會出現樹的高度很大的情況,從而導致各個操作的效率下降,極端情況下,二叉樹會退化為連結串列,為了解決這種複雜度退化的問題,需要設計一個平衡的二叉查詢樹。
平衡二叉查詢樹
平衡二叉查詢樹的定義是,二叉樹中的任意一個節點的左右子樹的高度相差不能大於1。完全二叉樹和滿二叉樹都是平衡二叉樹的一種。
平衡的意思就是讓樹的左右看起來比較對稱,不要出現左子樹很高,右子樹很低的情況,這樣就能讓整棵樹的高度儘可能的低一些,相對應的插入,刪除,查詢等操作效率也就高一些。
紅黑樹
紅黑樹英文名:Red-Black Tree 簡稱R-B Tree。是一種不嚴格的平衡二叉查詢樹。
紅黑樹上的節點,一類被標記為黑色,一類被標記為紅色,一般有一下特性:
- 每個節點是黑色或者紅色
- 跟節點是黑色的
- 每個葉子節點都是黑色的空節點(NULL)不存資料
- 任何相鄰的節點都不能同時為紅色,紅色節點是被黑色節點隔開的
- 每個節點從該節點達到其葉子節點的所有路徑
紅黑樹的操作
在插入和刪除結點的時候,上面的第三四五點就會被破壞,就不是一顆紅黑樹了,通過旋轉可以讓它重新成為紅黑樹。旋轉的目的就是為了保持紅黑樹的特性。
旋轉包括左旋,右旋,變色。
如圖:
左旋意味著把x結點變成一個左結點,它的左孩子變成它的父節點
右旋意味著把x結點變成一個右結點,它的右孩子變成它的父節點
變色就是節點的顏色由紅變黑或者由黑變紅
插入操作:
紅黑樹規定,插入的結點必須是紅色的,而且二叉查詢樹中新插入的結點都是放在葉子結點上。
- 如果插入的結點的父節點是黑色的,那麼我們什麼都不用做
- 如果插入的結點是跟結點,那麼直接改變它的顏色為黑色就可以
- 如果不是以上兩種情況,需要左右旋轉來改變顏色。
紅黑樹的平衡調整過程是一個迭代的過程,我們把正在處理的結點叫做關注結點,關注結點會隨著不停的迭代而發生變化,最開始的關注結點就是新插入的結點。
新插入結點之後,如果紅黑樹的平衡被打破,一般有三種情況,我們需要根據每種情況的特點迭代處理,不停的調整
(1)當前結點(被插入的結點)的父節點是紅色,並且當前結點的叔叔結點也是紅色
- 把父節點和叔叔結點設為黑色
- 把祖父結點設為紅色
- 把祖父結點視為當前結點,然後繼續對當前結點進行下面的2或者3操作
(2)叔叔結點是黑色,當前結點是其父節點的右子結點
- 把父節點當成新的當前結點
- 以當前結點為支點左旋
(3)叔叔是黑色,當前結點是左結點
- 把父節點設定黑色
- 把祖父結點設為紅色
- 以祖父結點為支點右旋
刪除操作
刪除操作的平衡調整分成兩部分
第一步:把它當成一個普通的二叉查詢樹,刪除節點,和正常的二叉查詢樹一樣的操作
1)被刪除的節點是葉子節點,直接刪除
2被刪除的節點只有一個子節點,刪除此節點,讓其子節點頂替它的位置
3)被刪除的節點有兩個子節點,先找到這個節點的右子樹中的最小節點,把它的內容 複製 到要刪除的節點上,然後在刪除掉這個最小節點,對於最小節點的刪除可以使用上面的兩個規則刪除
第二步:刪除之後可能會違背紅黑樹的特性了,通過旋轉和重新著色來修整它
開篇的時候說了紅黑樹的五個特性,如果一個節點刪除了,就可能違反紅黑樹的2,4,5這三個特性需要去解決。
假如我們刪除了一個黑色節點X,那麼就少了一個黑色節點,跟第5個特性不符合,我們可以假設在刪除的位置增加一個額外的黑色就能彌補失去的黑色了。這時候X節點就包含兩種顏色,紅黑或者黑黑,這時候又違背了特性1。這時候我們就從解決2,4,5三個問題變成了解決1,2,4三個問題。
解決1,2,4的問題,核心是把X節點所包含的黑色往根節點移動。
- 如果X是一個紅黑節點,就把X設定成黑色
- 如果X是黑黑節點並且指是根節點,啥也不用做
- 如果X是黑黑節點,並且不是根節點有四種子情況
3.1 X是黑黑節點,X的兄弟節點是紅色(這時候X的父節點和X的兄弟節點的子節點都是黑色)
<1>圍繞其父節點左旋
<2>X的父節點和祖父節點交換顏色(X的父節點設為紅色,祖父節點設為黑色)
<3>繼續從4種情況中找出合適的規則調整
3.2 X是黑黑節點,X的兄弟節點是黑色,X的兄弟節點的兩個孩子都是黑色
<1> 把X的兄弟節點變成紅色
<2> 把X的一個黑移到其父節點上
<3> 把X的父節點設定為新的X節點
<4> 繼續從4種情況中找出合適的規則調整
3.3 X是黑黑節點,X的兄弟節點是黑色,X的兄弟節點的左子樹是紅色,右子樹是黑色
<1> 圍繞X節點的兄弟節點右旋
<2> X的新兄弟節點和其右子節點交換顏色
<3> 繼續從4種情況中找出合適的規則調整
3.4 X是黑黑節點,X的兄弟節點是黑色,X的兄弟節點的右孩子是紅色,X的兄弟節點的左孩子是任意顏色
<1> 把X的父節點顏色賦值給X的兄弟節點
<2> 把X父節點設定為黑色
<3> 把X兄弟節點的右子節點設定為黑色
<4> 圍繞X的父節點左旋
<5> X中去掉一個黑色