紅黑樹
紅黑樹是一個含有紅黑節點並能夠自平衡的二叉查找樹
性質
必須滿足五個性質
- 每個節點要麼是黑色,要麼是紅色
- 根節點是黑色
- 每個葉子節點(NIL)是黑色的
- 每個紅色節點的兩個子節點一定是黑色的
- 任意一節點到其自身的每個葉子節點的路徑都包含相同數量的黑色節點
由性質五可推出:如果一個節點存在黑子節點,那麼該節點一定有兩個子節點
下圖是一個基礎的紅黑樹結構
紅黑樹並不一定是完美的平衡二叉查找樹,如上圖左子樹就比右子樹高,但是它符合性質五:任意一節點到每個葉子節點的路徑都包含相同數量的黑色節點
,稱這種平衡爲黑色完美平衡
我們把正在處理的節點稱爲當前節點(D),他的父親叫做父節點(F),父親的父親叫做祖父節點(P),父親的兄弟叫做叔叔節點(V),當前節點的兄弟叫做兄弟節點(K)
自平衡
紅黑樹自平衡的三步操作
變色:由紅色節點向黑色節點轉化,或者由黑色節點向紅色節點轉化
左旋:以某個節點作爲支點(旋轉節點),其右子結點變爲旋轉節點的父節點,右子結點的左子節點變爲旋轉節點的右子節點,左子節點保持不變
右旋:以某個節點作爲支點(旋轉節點),其左子節點變爲旋轉節點的父節點,左子節點的右子節點變爲旋轉節點的左子節點,右子節點保持不變
以上只是對左旋右旋的簡單示例,並沒有達到 黑色完美平衡
,至於是紅黑樹是如何自平衡的則需要看插入操作
查詢
- 從根節點開始查找,把根節點設置爲當前節點;
- 若當前節點爲空,返回null
- 若當前節點不爲空,用當前節點的key跟查找key作比較
- 若當前節點key等於查找key,那麼該key就是查找目標,返回當前節點
- 若當前節點key大於查找key,把當前節點的左子節點設置爲當前節點,重複步驟2
- 若當前節點key小於查找key,把當前節點的右子節點設置爲當前節點,重複步驟2
插入
- 從根節點開始查找,把根節點作爲當前節點
- 若當前節點爲null則插入,結束
- 若當前節點key等於查找key,那麼該key所在節點就是插入節點,更新節點的值,結束
- 若當前節點key大於查找key,把當前節點的左子節點設置爲當前節點,重複步驟2
- 若當前節點key小於查找key,把當前節點的右子節點設置爲當前節點,重複步驟2
插入的節點一定是紅色的,因爲如果插入節點的父節點是黑色的,那麼插入紅節點不會破壞黑色完美平衡(性質五),如果插入的是黑色,那麼插入位置所在的子樹黑色總是多1,必須要自平衡
插入分成了幾種情況
(一)紅黑樹爲空數,將插入節點設置爲根節點並置爲黑色
(二)插入節點的Key已存在,則更新已存在節點的Value爲插入節點的Value
(三)插入節點的父節點是黑色的,則直接插入,不會影響平衡
(四)插入節點的父節點是紅色的
由性質二可知:根節點是黑色的。如果插入節點的父節點是紅色的,那麼插入節點的父節點不可能是根節點,也就是說插入節點一定會有祖父節點,並且祖父節點是黑色的,記住這點,後續的自旋操作需要祖父節點參與。
定義 I 爲插入節點,P 爲祖父節點,F 爲父節點
4.1 叔叔節點存在並且爲紅節點
已知祖父節點是黑色,所以現在插入子樹的結構是黑紅紅
- 將父節點(F)和叔叔節點(V)設置爲黑色
- 將祖父節點(P)設置爲紅色
- 將祖父節點設置爲當前插入節點
這種情況,如果P節點的父節點是黑色,那麼就不需要再做處理。若果P節點的父節點是紅色的,那麼紅黑樹就不平衡了,需要把P設置爲插入節點繼續做自平衡。如果P節點就是根節點那麼需要將P節點設置爲黑色,這個時候就變成了黑黑紅,也是唯一一種會增加黑節點數的場景。
4.2 叔叔節點不存在或者爲黑節點,並且插入節點的父節點是祖父節點的左子節點
4.2.1 插入節點是其父節點的左子節點(LL)
Left-Left 父節點和插入節點都紅色的是左子節點
- 將父節點(F)設置爲黑色
- 將祖父節點(P)設置爲紅色
- 對祖父節點(P)進行右旋
這種情況,父節點爲紅節點,那麼叔叔節點非紅即爲NIL。因爲如果叔叔節點爲黑節點,而父結點爲紅結點,那麼叔叔結點所在的子樹的黑色節點就比父節點所在子樹的多了,違背了性質五。
4.2.2 插入節點是其父節點的右子節點(LR)
Left-Right 父節點是紅左子節點,插入節點是紅右子節點
- 對父節點(F)進行左旋
- 把父節點(F)設置爲插入結點,得到情景4.2.1
- 進行情景4.2.1的處理
4.3 叔叔節點不存在或者爲黑節點,並且插入節點的父節點是祖父節點的右節點
4.3.1 插入節點是其父節點的右子節點(RR)
Right-Right 父節點和插入節點都紅色的是右子節點
其實就是和LL做相反操作
- 將父節點(F)設爲黑色
- 將祖父節點(P)設爲紅色
- 對祖父節點(P)進行左旋
4.3.2 插入節點是其父節點的左子節點(RL)
Right-Left 父節點是紅右子節點,插入節點是紅左子節點
其實就是和LR做相反操作
- 對父節點(F)進行右旋
- 把父節點(F)設置爲插入結點,得到情景4.3.1
- 進行情景4.3.1的處理