演算法與資料結構--簡析紅黑樹

雲逸_發表於2018-09-05

1.為什麼要使用紅黑樹:

可以保證在 O(logN)的時間複雜度下做查詢 刪除 新增

2.性質:

(來自於維基百科Red–black tree條目 )

  1. 節點是紅色或者黑色的(Each node is either red or black)
  2. 根是黑色的,有時會被省略,由於根是黑色和紅色對規範並沒有其他影響(The root is black. This rule is sometimes omitted. Since the root can always be changed from red to black, but not necessarily vice versa, this rule has little effect on analysis)【這個特性與很多其他地方不同,但是從英文維基的解釋,根節點是紅是黑確實沒有什麼影響】
  3. 所有葉子是黑色的(葉子都是 nil 節點)
  4. 如果節點是紅色,那他的子節點一定是黑色(If a node is red, then both its children are black)
  5. 一個節點的所有到 nil 葉子節點的路徑都包含同樣的黑色節點(Every path from a given node to any of its descendant NIL nodes contains the same number of black nodes)

2.1 其他定義:

  1. 從根節點到某個節點的路徑中,包含黑色節點的數量,叫做黑色深度
  2. 從根到葉子的所有路徑中的黑色節點的統一數量被稱為紅黑樹的黑色高度

3.紅黑樹的關鍵屬性:

  1. 從根到最遠葉子的路徑不超過從根到最近葉子的路徑的兩倍,可以使樹大致高度平衡。由於,對樹進行增 刪 查的操作的最壞時間是和樹的高度緊密相關的,基於這個關鍵屬性,可以保證紅黑樹在最壞情況下仍然能保證效率
  2. 可以從性質4 5得知,紅黑樹是怎麼保證這個關鍵屬性。由於插入的所有節點都是紅色的,由於性質4,插入節點的父節點是不允許為紅色的。又因為性質5,黑色高度是相同的,所以從任意一點出發,最長的可能路徑是2*B 即紅黑交替的情況,最短則是完全不包含紅色點的路徑。

4.操作

4.1加入新節點

  • 根據規則4 新加的節點都是紅色的
  • 加入處理順序如下:(基於維基百科的演算法)
    • 步驟1:
      • 條件:樹是空樹
      • 操作:直接將節點放入根節點。
    • 步驟2:
      • 條件:父節點是黑色
      • 操作:直接將節點加入父節點的空葉子節點
      • 說明:因為父節點是黑色沒有違反性質4【從這裡往下,說明父節點是紅色的】
    • 步驟3:
      • 條件:如果叔父節點存在,並且也是紅色,
      • 操作:將父節點和叔父節點都改為黑色,然後將祖父節點改為紅色。為了保證祖父節點往上也不違反特性5,就會將祖父節點傳入,重複步驟1到3,直到是根節點【步驟1】或者是根節點的子節點【步驟3】
      • 目的:因為直接加入節點後,違反了特性4 。這時候如果叔父節點為 nil 或者黑色,直接修改父節點顏色是會違反性質5
    • 步驟4:
      • 條件:叔父節點為 nil 或者黑色
      • 操作:
        • ①如果接入節點和其父節點是左右或右左形態,則直接對其進行左旋和右旋,以達到下一步操作的要求。
        • ②如果加入節點和其父節點是以左左或者右右形態。則直接對應右旋,左旋來操作。旋轉完成後,由於加入節點的父親是紅色,祖父是黑色;所以違反了性質4,此時,將父親節點改為黑色,又會導致叔父子樹多一個黑色節點,所以將被旋轉下去的祖父變為紅色。
      • 目的:通過這種方法進行操作,是基於 AVL 的平衡旋轉方案,減少了樹的高度。

4.2刪除節點:

  • 通常, 在二叉搜尋樹(紅黑樹屬於此型別)中,刪除一個節點,需要找到他左子樹的最大節點或者是右子樹的最小節點,將值與需要被刪除的值的節點替換,並刪除找出這個值的節點。所以,刪除一個有兩個子節點的節點的場景可以簡化為刪除一個節點只有一個子節點的場景。

  • 場景一:刪除紅色節點,那由性質4可得知。此節點的父子節點都是黑色。所以我們可以簡單的使用他的黑色兒子來替換它

  • 場景二:被刪除的節點他兒子是紅色的,如果只是去刪除這個黑色節點,用紅色的兒子頂替的話,會違反性質5.但是如果我們把替換後的兒子變為黑色,則通過他的路徑都會通過他的黑色兒子。

  • 場景三:當要刪除的節點和他的兒子兩個都是黑色的時候,這是複雜情況。下面將會分類討論。出於方便,稱呼這個兒子為N(在新的位置上),稱呼它的兄弟(它父親的另一個兒子)為S。我們使用P稱呼N的父親,SL稱呼S的左兒子,SR稱呼S的右兒子。【場景裡實際上需要刪除的節點已經被刪除】。

  • 處理順序如下:

    • 步驟1.
      • 條件:如果 N 是根節點,因為根節點為什麼顏色都不違反紅黑樹的性質,所以直接操作完畢。
    • 步驟2.【下面的情形,S 都是黑色,只是對 S 兒子不同顏色進行不同操作】——為4 5 6步驟做準備
      • 條件:S 是紅色,
      • 操作:對 N 的父親進行旋轉,旋轉方向是 S 的反方向【即S 是 P 的左兒子則對 P 右旋】。旋轉完成後 S 就變成了 N 的祖父。接著我們把 N 的父親和祖父顏色進行對調【即將 S 和 P 的顏色對調】。
      • 目的:因為被刪除的節點和 N 都是黑色的。所以 N 被刪除以後已經違反了性質5。此時,通過旋轉將原來是紅色的 S 放到 N 和 P 的通過的路徑上,並將 P 的顏色和 S 顏色進行對調。這樣,對未被影響的一邊的子樹的黑色節點數沒有任何影響,因為 P 和 S 是對調了顏色,另外一邊也未受影響。因為旋轉的影響,之前 S 子樹缺少的黑色節點變成了SL 和SR兩個子樹各缺少一個黑色節點【因為P 和 S 的顏色對調了,所以這2個子樹的上游的黑色節點並沒有受影響】接下來就交給步驟4 5 6繼續處理。【有一點比較疑惑,其實2和3交換順序,並不會影響處理邏輯,而如果2放到後面,後面連續處理起來會更加的清晰。】
    • 步驟3——通過減少一個黑節點來使樹符合條件
      • 條件:如果 P S 和 S 的兒子 都是黑色,
      • 操作:我們直接將 S 變為紅色。然後將再從 P 開始步驟1的處理,直到到根。
      • 目的:因為之前被刪除的節點是黑色,所以這個子樹少了一個黑色節點。所以當 S 的 父親和兒子都是黑色的時候,就解決了不符合性質5的情況,而且因為父親兒子都是黑色,也是符合性質4的。但是,實際上 P 的兄弟也是少了一個黑色節點,所以需要遞迴的往上進行處理,直到根截止。
    • 步驟4.
      • 條件:S 和 S 的兒子都是黑色,但是 P 是紅色。
      • 操作:交換 S 和 P 的顏色。
      • 目的:這不影響不通過N的路徑的黑色節點的數目,但是它在通過N的路徑上對黑色節點數目增加了一,添補了在這些路徑上刪除的黑色節點
    • 步驟5:
      • 條件:S 是黑色; N 是左兒子時,S 的左兒子是紅色,右兒子是黑色;N 是右兒子時,S 的左兒子是黑色,右兒子是紅色;
      • 操作:當 N 是左兒子時對 S 右旋,當 N 是右兒子時對 P 左旋。然後交換 S 和對應被提升成父節點的兒子的顏色。【即S 的兒子變成黑色,S 變成紅色】
      • 目的:為了給步驟6做準備,因為如果直接旋轉S,將 S 作為 P 的父節點時,S 的某一邊的紅色兒子會被轉移到 P 的兒子位。此時,如果 P 是紅色則違反了性質4,【兩個連續紅色】如果 P 是黑色,則違反了性質5。原來 S 的兒子少了一個黑色的路徑
    • 步驟6:
      • 條件: S 是黑色的,S 的某一個兒子是紅色的【可能是步驟5旋轉後又對調顏色後的 S】。
      • 操作: P 做對 S 紅色反方向的選擇【即右紅左轉】。然後交換 P 和 S 的顏色,並讓之前紅色的孩子變為紅色
      • 目的:把黑色的 S 旋轉成為P 的父親,並吧黑色交換給了 P。這時候相當於給 N 子樹補齊了之前刪除的黑色節點數,同時紅色節點是S 子樹的最高節點。將他變成黑色後,因為 S 黑色被換走後也被補充回來了。同時,P 與 S 對調了顏色。不管現在 S 是什麼顏色P 和 S 兒子這2個子樹的黑色節點數都是相同的。

相關文章