一棵二叉查找樹若是知足下面的紅黑性質,則爲一棵紅黑樹:測試
1) 每一個結點或是紅的,或是黑的。spa
2) 根節點是黑的。3d
3) 每一個葉結點(NIL)是黑的。指針
4) 若是一個結點是紅的,則它的兩個兒子都是黑的。code
5) 對於每一個結點,從該結點到其子孫結點的全部路徑上包含相同數目的黑結點。blog
引理:一棵有n個內結點的紅黑樹的高度至多爲2log2(n+1)。遞歸
左旋以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 }
首先,看一下插入僞代碼,與二叉查找樹樹相似:
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 }
測試結果如圖: