紅黑樹

1、紅黑樹性質

一棵二叉查找樹若是知足下面的紅黑性質,則爲一棵紅黑樹:測試

1)  每一個結點或是紅的,或是黑的。spa

2)  根節點是黑的。3d

3)  每一個葉結點(NIL)是黑的。指針

4)  若是一個結點是紅的,則它的兩個兒子都是黑的。code

5)  對於每一個結點,從該結點到其子孫結點的全部路徑上包含相同數目的黑結點。blog

引理:一棵有n個內結點的紅黑樹的高度至多爲2log2(n+1)。遞歸

2、旋轉

  左旋以x到y之間的鏈爲「支軸」進行。它使y成爲該子樹新的根,x成爲y的左孩子,而y的左孩子成爲x的右孩子。class

左旋的僞代碼以下:程序

 1 LEFT-ROTATE(T,x)
 2     y <- right[x]             ;Set y
 3     right[x] <- left[y]      ;Turn y's left subtree into x's right subtree
 4     if  left[y]!=NIL[T]
 5         then p[left[y]] <- x
 6     p[y] <- p[x]              ;Link x's parent to y
 7     if p[x]=NIL[T]
 8        root[T]  <-y
 9    else if x=left[p[x]]
10              left[p[x]] <- y
11    else right[p[x]] <-y
12   left[y] <- x
13   p[x]  <-y

旋轉示意圖以下:方法

 代碼以下:

 1 enum RB_Color
 2 {
 3     RED,
 4     BLACK
 5 };
 6 
 7 struct  RB_Tree
 8 {
 9     int key;
10     RB_Tree *Left;
11     RB_Tree *Right;
12     RB_Tree *Parent;
13     RB_Color Color;
14 };
15 
16 /*紅黑樹左旋操做
17 操做步驟:
18 1.以e與R[e]之間的鏈爲「支軸」進行。
19 2.使得R[e]成爲該子樹新的根,e成爲R[e]的左孩子,而R[e]的左孩子則成爲x的右孩子。
20 */
21 void LEFT_ROTATE(RB_Tree * &root,RB_Tree *e,RB_Tree *NIL)
22 {
23     if(root==NIL||e==NIL)
24         return ;
25     //獲得結點的右子結點
26     RB_Tree * RightChild=(*e).Right; 
27     //把右子結點的左子結點賦值給結點右子結點
28     (*e).Right=(*RightChild).Left;  
29     //若是右子結點的左子結點不爲空,把其指向此結點
30     if((*RightChild).Left!=NIL)    
31         (*((*RightChild).Left)).Parent=e;
32     //此結點的右子結點的父節點指向此節點的父節點
33     (*RightChild).Parent=(*e).Parent; 
34     if((*e).Parent==NIL)
35         root=RightChild;
36     else  if(e==(*((*e).Parent)).Left)
37         (*((*e).Parent)).Left=RightChild;
38     else
39         (*((*e).Parent)).Right=RightChild;
40     //把此節點放到其右結點的左結點上
41     (*RightChild).Left=e;
42     //把此節點的父節點指向其右結點
43     (*e).Parent=RightChild;
44 }
45 
46 /*
47 紅黑樹的右旋操做
48 */
49 void RIGHT_ROTATE(RB_Tree * &root,RB_Tree *e,RB_Tree *NIL)
50 {
51     if(root==NIL||e==NIL)
52         return ;
53     RB_Tree * LeftChild=(*e).Left;
54     (*e).Left=(*LeftChild).Right;
55     if((*LeftChild).Right!=NIL)
56         (*((*LeftChild).Right)).Parent=e;
57     (*LeftChild).Parent=(*e).Parent;
58     if((*e).Parent==NIL)
59         root=LeftChild;
60     else  if(e==(*((*e).Parent)).Left)
61         (*((*e).Parent)).Left=LeftChild;
62     else
63         (*((*e).Parent)).Right=LeftChild;
64     (*LeftChild).Right=e;
65     (*e).Parent=LeftChild;
66 }

3、插入

首先,看一下插入僞代碼,與二叉查找樹樹相似:

BR-INSERT(T,x)
    y <-NIL
    x <-root[T]
    while x!=NIL
           do y <- x
            if key[z]  <  key[x]
                then x <- left[x]
                else  x <- right[x]
     p[z] <-y
     if y=NIL
      then root[T] <- z
      else if key[z] <key[y]
      then left[y] <- z
      else right[y] <-z
      left[z] <-NIL
      right[z] <-NIL
      color[z]  <-RED
      RB-INSERT-FIXUP(T,x)

因爲插入了一個紅色的結點有可能會破壞紅黑的性質。

性質1和性質3會繼續保持,由於新插入的結點的子女都是哨兵NIL.性質5即從一個指定結點開始的每條路徑上黑結點的個數都是相等的,也會成立,由於結點z代替了哨兵,而且結點z自己是具備哨兵的子女的紅結點。所以,惟一可能被破壞的就是根節點須要爲黑色的性質2,以及一個紅結點不能有紅子女的性質4。若是z是根節點則破壞了性質2,若是z的父節點是紅色就破壞了性質4。例以下圖破壞了性質4。

插入結點z後,破壞了紅黑樹的性質4.具體的處理方法分爲如下三類:

狀況1:z的叔叔y是紅色的。如上圖所示。這種狀況的處理方法wie,把插入結點(z)的父節點和叔父結點設置爲黑色,而後z指針上移兩層,指向其祖先結點,並把新指向的結點變爲紅色,遞歸處理新指向的結點。以下圖所示。

第二種狀況:z的叔叔y是黑色的,並且z是右孩子,如上圖所示。

經過一次左旋,變成第三種狀況,左旋後以下圖所示。

第三種狀況:z的叔叔y是黑色的,並且z是左孩子,如上圖。

程序源代碼以下:

 1 /*
 2 保持紅黑樹的性質
 3 */
 4 void RB_INSERT_FIXUP(RB_Tree *&root,RB_Tree *e,RB_Tree *NIL)
 5 {
 6     RB_Tree * uncle=NIL;
 7     while ((*((*e).Parent)).Color==RED)
 8     {
 9         //若是結點的父節點爲其祖先結點的左孩子
10         if((*e).Parent==(*(*((*e).Parent)).Parent).Left)
11             uncle=(*(*((*e).Parent)).Parent).Right; //得到其叔父結點
12         else             
13             uncle=(*((*e).Parent)).Left;
14         //若是其叔父結點爲紅色
15         if((*uncle).Color==RED)
16         {
17             (*((*e).Parent)).Color=BLACK;
18             (*uncle).Color=BLACK;
19             (*(*((*e).Parent)).Parent).Color=RED;
20             e=(*((*e).Parent)).Parent;
21         }
22         //其叔父爲黑色,
23         else 
24         {
25             //此結點爲其父節點的右子結點
26             if(e==(*((*e).Parent)).Right)
27             {
28                 e=(*e).Parent;
29                 LEFT_ROTATE(root,e,NIL);
30             }
31             (*((*e).Parent)).Color=BLACK;
32             (*(*((*e).Parent)).Parent).Color=RED;
33             RIGHT_ROTATE(root,(*((*e).Parent)).Parent,NIL);
34         }
35     }
36     (*root).Color=BLACK;
37 }
38 
39 /*
40 向紅黑樹中插入元素
41 */
42 void RB_INSERT(RB_Tree *&root,RB_Tree *e,RB_Tree *NIL)
43 {
44     if(e==NIL)
45         return ;
46     RB_Tree * parent=NIL;
47     RB_Tree *current=root;
48     while (current!=NIL)
49     {
50         parent=current;
51         if((*current).key<(*e).key)
52             current=(*current).Right;
53         else
54             current=(*current).Left;
55     }
56     (*e).Parent=parent;
57     if(parent==NIL)
58         root=e;
59     else
60     {
61         if((*e).key<(*parent).key)
62             (*parent).Left=e;
63         else 
64             (*parent).Right=e;
65     }
66     (*e).Left=NIL;
67     (*e).Right=NIL;
68     (*e).Color=RED;
69     RB_INSERT_FIXUP(root,e,NIL);
70 
71 }

測試結果如圖:

相關文章
相關標籤/搜索