說明
紅黑樹聽起來挺嚇人,但當你看完這篇文章後再加上紅黑樹在線生成練習幾次,就能夠輕鬆拿下。
下面的教程中x
代表當前插入的節點,xp
,xpp
,xppl
,xppr
,uncle
,nephewF(far)
,nephewC(close)
分別爲父節點,祖父節點,祖父節點的左孩子,祖父節點的右孩子,叔叔節點,遠侄子節點,近侄子節點。
左旋:逆時針旋轉
右旋:順時針旋轉
reBalanceAfterInsert():我們在插入後會調用此方法進行再平衡
reBalanceAfterDelete():我們在刪除節點後會調用此方法進行再平衡
紅黑樹的約束
1,根節點必須爲黑色。
2,紅色節點不能有紅色節點的孩子
3,從根節點出發到達葉節點所經過的黑色節點個數相同
4,節點必須爲紅色或黑色
5,一般插入節點爲紅色
數據結構
class RBTreeNode{
boolean red;
int val;
RBTreeNode left;
RBTreeNode right;
RBTreeNode parent;
}
查找
紅黑樹本身就是一種二叉查找樹,所以可以進行二分查找。這與AVL樹或者BST是一致的。
插入
插入時有幾種基本情況,更多複雜的情況都可以轉換成基本情況求解(重點)。左右都是鏡像對稱的。這裏採用右邊插入爲例子。
基本情況1
當前樹爲空,則要插入的節點就是根節點,因此直接插入並將顏色置爲黑。
基本情況2
要插入的節點的父節點爲黑色,此時直接插入節點。因爲增加一個紅色節點不會影響當前插入路徑上黑色節點個數變化。
基本情況3(三點一線)
當要插入的節點存在祖父節點,且不存在叔叔節點時,判斷x
和xp
的位置關係,再判斷xp
和xpp
的位置關係。如果是三個節點中一條線上時,稱爲三點一線
。此時我們要做的就是以xp
爲圓心,xpp
進行左旋。在左旋完成後將xp
與xpp
顏色交換(暴力認爲xp
置爲黑色,xpp
置爲紅色)
if(xp.right == x){
// 再判斷xpp
if(xpp.right == xp){
// 此時是圖中的情況,這裏稱爲三點一線
}
}
基本情況4(之字形)
這個情況和基本情況3是類似的。只是結構上不再是三點一線
而是一種之字形
處理方式是,將x
和xp
根據位置情況進行旋轉,然後轉換成情況3。當x
是xp
的左孩子時,以x
爲圓心xp
進行右旋。然後形成三點一線
狀態,此時xp
位置就是x
了,再以x爲圓心,xpp
進行左旋,變色即可。
基本情況5
當uncle
存在且爲紅色時,直接將uncle
和xp
變爲黑色,xpp
變爲紅色,同時再調用reBalanceAfterInsert()
判斷對剩餘樹顏色的影響。從圖中看到,在xpp
變爲紅色後會進行reBalanceAfterInsert()
將根節點xpp
染成黑色。
基本情況6
當uncle
存在且爲黑色時,只需要以xp
爲圓心xpp
進行旋轉並將uncle
置爲紅色即可。圖中是先進行了情況5的調整,然後再進行情況6的調整。
刪除
對於刪除來說需要考慮的比插入多一點情況,但是也不難。
首先看下整顆紅黑樹形態
基本情況1
刪除的節點爲紅色節點,直接刪除即可,比如刪除550
。
基本情況2
刪除的節點爲黑色,且無左右子節點。比如刪除50
。此時需要判斷nephewF
也就是遠侄子節點是否存在,對於上圖50
的nephewF
節點爲160
。此時遠侄子節點存在且爲紅色,那麼需要以兄弟節點 150
爲圓心,xp 100
繞其進行左旋。然後將100
和160
染成黑色。
基本情況3
遠侄子不存在,但是近侄子存在且爲黑色。這裏處理方式和插入時情況4
轉成情況3
是一樣的。以近侄子爲圓心,兄弟節點繞其旋轉,變成遠侄子的情況,再使用情況2進行處理。
基本情況4
當出現當前節點無孩子,且存在兄弟節點也無孩子,且父節點爲黑色時。
此時假如要刪除150
,在刪除後,左子樹路徑黑色節點個數少一個,因此需要將右邊路徑也-1,那麼最快的方法就是將160
設置爲紅色,那麼此時對於更大一層的樹而言,155
節點那條路上也少了一個黑色節點,則需要再一次調用reBalanceAfterDelete()
。可以明顯的看出以155
爲調整的節點來說,存在遠侄子節點450
,那麼就轉換成情況2。則接下來會以350
爲圓心,175
繞其進行左旋,然後將250
置爲175
的右孩子,350
變成根節點,然後450
被染成黑色。調整結束。
基本情況5
當存在兄弟節點並且父節點爲紅色時。如圖
此時假設刪除200
,那麼刪除後左邊路徑黑色節點個數少一個,應該將右邊節點個數也-1,那麼就將300
置爲紅色,然後250
置爲黑色。調整結束。可以發現在經過上面方法調整後,並沒有對更大層面產生影響。
基本情況6
當節點只有一個孩子且爲紅色時,直接將xp
指向child
,同時child
顏色變成黑色。以刪除155
爲例。
![在這裏插入圖片描述](https://img-blog.csdnimg.cn/20200322200355860.png
基本情況7
當存在左右孩子時,找到左子樹中最大的節點C
,將其值賦給x
,然後轉換成對C
的刪除操作。
這裏以刪除350
爲例,首先找到300
,然後將300
的值賦給350
節點,然後變成對300
這個紅色節點的刪除。
可能這個例子太簡單了,這邊還有一個例子供大家學習。
首先找到左子樹最大節點250
,然後將250
的值賦給300
,再變成對250
的刪除,此時發現存在近侄子情況,因此是情況3。將以近侄子160
爲圓心,155
繞其進行一次左旋,隨後變成情況2,再將175
以160
爲圓心進行右旋,調整結束。