紅黑樹的插入2(共四篇)

  紅黑樹的插入的時間複雜度是O(lgn),插入操作和普通的二叉搜索樹幾乎一模一樣,不同點是在插入後需要看看紅黑樹的五大性質是否被破壞了,如果被破壞了,那就需要調整了。下面先來看看紅黑樹插入操作的代碼,其實插入操作很簡單,假如要插入的節點叫做x,無非就是找到從根節點出發,發現當前節點比x大的走左邊路線,發現當前節點比x小的走右邊路線,一直走到葉子節點,然後插入這個x節點。關鍵點是插入後的調整。下面先看看紅黑樹的插入僞代碼(抄自算法導論)

RB-INSERT(T, x)
  y = T.NIL;
  z = T.root;
  // 一直循環直到找到z的合適位置
  while z != T.NIL
    y = z;
    if x.key < z.key
       z = z.left;
    else z = z.right;
  x.p = y;
  if y == T.NIL
     T.root = x;
  elseif x.key < y.key
     y.left = x;
  else y.right = x;
  x.left = T.NIL;
  x.right = T.NIL;
  x.color = RED;
  // 調整恢復紅黑樹的五大性質
  RB-INSERT-FIXUP(T, x);

  注意到上面我們插入的節點染爲紅色,爲什麼不是黑色呢?因爲如果黑色的話,調整起來會複雜一些,具體我們後面看刪除操作就明白了。下面我們分析一下插入紅色節點x後整棵紅黑樹的變化。首先性質1、3、5肯定是沒有變化的。僅可能破壞性質2(x爲根節點)和性質4(父節點是紅色的)。性質2破壞很好解決,直接染黑就是了。我們重點來討論性質4被破壞後,如何調整和恢復。這裏先說一個指導思想,1.調整恢復過程中千萬不能破壞其他性質,否則只會帶來更多難以解決的問題,2.指針指向的是需要調整的節點,只要我們能把指針指向的節點的父節點調整爲黑色,並且沒有破壞性質5,那麼問題就解決了。那麼總結一下,插入後的所有情況值可能有如下五種

 

  case 0.插入節點z爲根節點——這種情況好辦,直接染黑完事,我們不討論

  case 1.父節點爲黑色節點——這種情況最好辦,根本沒有破壞紅黑樹的五大性質,不需要做任何調整,我們不討論

  case 2.父節點爲紅色節點,叔節點也爲紅色

  case 3.父節點爲紅色節點,叔節點爲黑色節點,當前節點爲右孩子

  case 4.父節點爲紅色節點,叔節點爲黑色節點,當前節點爲左孩子

就以上五種情況已經列舉了所有可能,我們不關心兄弟節點的顏色,爲什麼呢?這裏可留給大家思考一下,稍微想下就明白了。下面用一個圖展示所有需要討論的情況:

  

  

 下面我們一個情況一個情況地來分析。

  先看case 2:

 可以看到 父節點和叔節點都是紅色,再想想我們的指導思想2,要把父節點染黑,那就染黑節點p,這樣子的話z子樹的黑高就加1了,那只有把節點a染紅,同時不能讓w子樹黑高減一,那就把w染黑,這樣z的父節點就成功染黑了,修復了性質4,而且沒有破壞性質5。那麼上圖的紅黑樹的子樹性質就恢復了。此時指針指向節點a,再看看a往上的子樹的五大性質是否有保持,此時情況可能轉case1,2,3,4,如果轉了case1,那麼性質就恢復了。看看僞代碼,僞代碼如下:

 

// case 2僞代碼
// 叔節點
y == x.p.p.right;
if y.color == red
  x.p.color = black;
  y.color = black;
  x.p.p.color = red;
  x = x.p.p;

 

 

接着來看case 3:

 

 這個case 3比較有趣,父節點爲紅色,叔節點爲黑色,當前節點爲右孩子。一看兩個紅節點,怎麼旋轉都不會破壞紅黑樹原有的性質,而且旋轉一下,把當前節點指向節點p的話,情況就變得和case 4一模一樣了。變爲了父節點(z)紅色,叔節點爲黑色,當前節點爲左孩子。這樣正好我們把case 3和case 4合併起來了,只要解決case 4即可。看看case 3僞代碼,只需兩行:

 

// case 3
x = x.p;
LEFT-ROTATE(T, x)

 

 

那麼接下來看看case 4

case 4中我們一步步的分析如何從左邊的圖調整爲右邊的圖,首先還是回到我們的指導思想,把x指針指向節點的父節點染黑,染黑後發現改變了子樹Q和W的黑高,那麼一個做法就是右旋轉a節點,右旋節點a後發現子樹F和G的黑高加了1,破壞了性質5,那麼把節點a染紅,正好就把黑高調整回來了,經過這樣的調整,也就變成了上面右邊的紅黑樹圖案了。至此,性質4恢復了,紅黑樹的插入調整也正常結束。case 4僞代碼:

 

// case 4
x.p.color = black;
x.p.p.color = red;
RIGHT-ROTATE(T, x.p.p);

至此紅黑樹的插入操作分析完畢。下面整理一段整個插入流程的僞代碼,以方便大家全局觀看插入操作:

 

 

// 紅黑樹插入邏輯
RB-INSERT-FIXUP(T, x)
while x.p.color = red
  if x.p == x.p.p.left
    y == x.p.p.right;
    if y.color == red
      // case 2
      x.p.color = black;
      y.color = black;
      x.p.p.color = red;
      x = x.p.p;
    else if  x == x.p.right
      // case 3
      x = x.p;
      LEFT-ROTATE(T, x)
    // case 4
    x.p.color = black;
    x.p.p.color = red;
    RIGHT-ROTATE(T, x.p.p);
  else(x.p爲右子樹,和左子樹操作相反即可);
T.root.color = black;

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章