《演算法導論》筆記 紅黑樹(一)

2021-08-29 19:43:32 字數 3290 閱讀 9579

滿足下面幾個條件的二叉搜尋樹,稱為紅黑樹:

1.任何乙個節點都被著色――紅色或是黑色。

2.根節點是黑色的。

3.所有的nil節點都看成黑色(nil節點是就是乙個假想的或是無實在意義的節點,所有應該指向null的指標,都看成指向了nil節點。包括葉節點的子節點指標或是根節點的父指標)。

4.如果乙個節點是紅色的,那麼它的子節點一定是黑色的。

5.對於任何乙個節點而言,從該節點到它的子孫節點中的nil節點路徑中,所包含的黑節點個數相同。

黑高度的定義:

從任何乙個節點,向下到底部的路徑中,包含的黑節點的個數,稱為這個節點的黑高度。從紅黑樹的第5條性質可以看出,黑高度是唯一的、確定的。

只要同時滿足紅黑樹的這些條件,就一定會有「紅黑樹可以保證任何兩條從根部到樹葉的路徑節點個數相差不超過2倍」這個平衡的性質。

我們可以假設,如果存在兩條路徑l和s,l比s長兩倍以上(s路徑上有n個節點,l上有大於2n個節點)。可知,s的黑高度最大只可能是n,那麼根據第5條性質,l的黑高度也大也只可能是n,也就是,l路徑上一定有超過n個紅色節點(因為節點不是黑的就必定是紅的)。所以,肯定會有兩個以上的紅色節點是相鄰的。這就與第4個條件矛盾了。所以,紅黑樹可以保證任何兩條從根部到樹葉的路徑節點個數相差不超過2倍。

紅黑樹的高度h<2lg(n+1)(這個在《演算法導論》13.1節中有證明)。也就是說,紅黑樹的操作的時間複雜度是o(lgn)的。

二叉搜尋樹的操作時間複雜度,取決於樹高h,因此,我們當然就希望h盡量地小以提高操作的效率。從直觀上就可以發現,二叉搜尋樹各子樹的規模越平均,它的高度就會越小。所以在應用中,一般都會將二叉搜尋樹實現成一種平衡樹,以保證最差時間複雜度為o(lgn)。紅黑樹就是其中的一種,應用得很廣泛(sgi stl的set和map就是基於紅黑樹來實現,linux核心中也用到這個資料結構)

紅黑樹是二叉搜尋樹的一種。它與普通二叉搜尋樹不同的是,紅黑樹的每個節點都附加了另乙個屬性――顏色,可以是紅色,也可以是黑色。通過對於每條路徑上節點顏色的規則進行限定,紅黑樹可以保證任何兩條從根部到樹葉的路徑節點個數相差不超過2倍。所以,紅黑樹是一種近似平衡的二叉搜尋樹。

紅黑樹的查詢、最大值、最小值、前趨、後繼等操作,與普通的二叉搜尋樹沒有什麼區別。插入和刪除操作需要重新實現。僅僅用普通的二叉搜尋樹的插入和刪除動作,可能會破壞紅黑樹本身的一些性質,因此,需要進行額外的處理。這些額外的處理主要是改變樹節點的顏色,或是改變樹的結構。

樹的旋轉

改變樹的結構,主要是用旋轉。旋轉有兩種,左旋和右旋,下面是這兩種旋轉的: 

這種旋轉的操作,是在二叉搜尋樹的區域性對樹的結構進行調整的一種方式,經過旋轉之後,二叉搜尋樹的性質是不會發生改變的。如下圖:

向紅黑樹插入節點,先將需要插入的節點著成紅色,用普通二叉搜尋樹的方法將這個節點插入到樹中,然後再想辦法把被破壞的紅黑性質恢復。(將新插入的節點顏色著成紅色,呆以儘量減少對紅黑性質的破壞,恢復起來也容易。因為插入紅色節點的話,那麼被破壞的部分會在區域性,考慮的問題也就比較少,恢復過程也容易形成遞迴,詳細見下文說明)

我們考慮下,如果乙個紅色節點(下文稱用z

指向它)被插入到樹中,那麼有哪些紅黑性質可能被破壞呢?只有第2

條(根節點是黑色的)以及第4

條(紅色節點的子節點一定是黑色節點),其它都不會被破壞。

如果插入的節點的父節點是黑色的,那麼不需要做任何調整,這紅黑樹是正常的。如果父節點是紅色,或沒有父節點(也就是插在了樹根的位置),那麼就要進行調整以保證紅黑樹是正確的。

首先對第4

條進行分析,我們知道,插入乙個新的節點,這個節點肯定會被放到樹的底部成為乙個葉節點(參考普通二叉樹的插入過程),那麼這個紅色節點就沒有可能和自己的子節點同色(因為葉節點的子節點是nil

節點,都是黑色的),如果第4

條被破壞的話,肯定是z

指向的節點的父節點是紅色的。因此,為了使分析和解決更加容易和清晰,我們在對樹進行調整以恢復紅黑特性時,始終使得z

總是指向相鄰紅色的節點中的子節點(指標z

可能會向上公升到樹的中部或根部)。基於這個做法,我們可以知道,如果是第2

條被破壞了的話,也就是z

指向根節點了,那麼第4

條肯定就符合了(因為z

的父節點是nil

,黑色的),因此對第2

條性質的恢復變得很簡單,只需要被根從紅色變為黑色即可。這時,所有性質都會被滿足了(因為是根節點,所以根節點被著為黑色時,所有路徑都黑高會統一加1

,也就是第5

條不會被破壞,操作之後的紅黑樹仍然是正確的)。好了,現在只留下第4

條性質的恢復的問題了。

如果第4

條被破壞了,也就是說,z

的父節點是紅色的,那麼,說明,z

一定有祖父節點,而且是黑色的(否則插入前原樹就有問題,又或是調整時的方法不正確)。因此可以把問題放到以z

的祖父節點為根節點的子樹內進行解決,這樣可以把調整的範圍最小化,而且這也是有可能的:只要不改變這子樹的黑高度,那麼就不會對樹的其它部分產生影響。我們要做的就是在這個子樹範圍內把紅黑性質調整回來。再看子樹的根是否與其父節點同為紅色,是的話,就再次用前面所說的去解決它,一直向上遞迴到紅黑性質被恢復為止。

紅黑樹的節點插入

對第4條性質的恢復,根據z

的父節點是z

的祖節點的左子節點還是右子節點,分為兩組對稱的情況,每組有3

種情況。下面我們只對其中一組進行說明,以z

的父節點是z

祖節點的左子節點為例:

第一種:z

的「叔父」節點是紅色。

如上圖所示,在這種情況下,將父、叔節點都著為黑色,再將子樹根節點著為紅色,那麼子樹的黑高度沒有發生改變,而且紅黑性質得得到了調整。此時,再將z

指向子樹的根節點,向上遞迴恢復紅黑特性。

第二種:z

的「叔父」節點是黑色的,z

的父節點的左子節點。

如上圖,將z

的父節點與祖節點進行一次右旋,並把父節點著黑色,原來的祖節點著紅色。這些子樹的紅黑特性得到了恢復,而且子樹的黑高度沒有變化。另外,由於子樹根節點已經是黑色了(這個節點不會出現父子同為紅色的問題了),所以不必再向上遞迴了,此時整個樹的紅黑特性都已經是正確的了。

第三種:z

的「叔父」節點是黑色的,z

的父節點的右子節點。

如上圖,將z

本身與其父節點進行一次左旋,讓z

指向原來的父節點,就可以調整為情況二,而情況二已經得到解決。

這樣,紅黑樹的節點插入問題就得到了解決。

紅黑樹(演算法導論)

測試 所用的例子為算導第三版p179圖13 4 include using namespace std const bool black 0 黑色 const bool red 1 紅色 struct node 結點結構 class rb tree 初始化nil結點和root node left r...

演算法導論 紅黑樹

原文 組內培訓,講紅黑樹,找出演算法導論,啃了乙個週末,其中插入結點很簡單,刪除結點有點複雜,但跟著演算法導論上一步一步來沒有什麼問題。不想備份blog的,所以沒有把上穿。可直接察看ppt。紅黑樹性質 1.每個節點或是紅的,或是黑的 2.根節點是黑的 3.每個葉結點 nil 都是黑的 4.如果乙個結...

演算法導論學習筆記 紅黑樹

紅黑樹的5個性質 1 每個結點要麼是紅的,要麼是黑的。2 根結點是黑的。3 每個葉結點,即空結點 nil 是黑的。4 如果乙個結點是紅的,那麼它的倆個兒子都是黑的。5 對每個結點,從該結點到其子孫結點的所有路徑上包含相同數目的黑結點。public class rbtree 當在某個結點nodex上,...