紅黑樹(一):插入

  紅黑樹跟AVL樹同樣,也是平衡二叉樹,其查找、增長、刪除效率爲O(lgN),紅黑樹使用很是普遍,C++STL裏面的map,set都是用紅黑樹實現的。html

  如下就是一棵紅黑樹:算法

  

  《算法導論》上定義紅黑樹需知足如下五個性質:spa

  1.每一個結點是黑色或是紅色。指針

  2.根結點是黑色。code

  3.全部葉子節點是黑色(也就是上面Nil結點)htm

     4.若是一個節點是紅色的,則它的兩個子結點都是黑色的。(也就是說父子節點不能同時是紅色節點)blog

  5.對每一個節點,從該結點到到葉子節點的路徑上,均包含相同數目的黑色節點。遞歸

  須要說明一下的是,以上Nil結點是」哨兵節點「,將全部子節點爲空的指針都以此Nil節點來代替,並且根節點的父節點也是Nil節點。對於第五條,任一節點到Nil節點的黑色結點個數(包含自己及Nil節點)都相同,也就是所謂的黑高相同。get

  先來定義紅黑樹的節點,紅黑樹須要一個指示顏色的屬性,用一個位bool值來表示就能夠了,同時,紅黑樹還須要根據父節點的顏色來修復紅黑樹性質,所以須要指向父節點的指針。博客

1 struct RBTreeNode {
2     RBTreeNode* pParent;
3     RBTreeNode* pLeft;
4     RBTreeNode* pRight;
5     bool isRed;
6     int nData;
7 };

  同時,定義紅黑樹的顏色值,以及Nil節點:

1 #define RBTreeNodeBlack false
2 #define RBTreeNodeRed true
3 #define RBTreeNodePtr RBTreeNode*
4 #define RBTreeNilNodePtr &RBTreeNilNode

  本節討論紅黑樹的插入,由於紅黑樹也是二叉搜索樹,全部紅黑樹的插入也遵循二叉搜索樹插入的過程,只是最後爲了保證紅黑樹的性質,須要在插入後對樹進行修正,就像對AVL樹執行插入後再再平衡同樣。

  先來考慮一個問題,新插入一個節點時,這個新插入的節點是用紅色的好仍是黑色好?根據紅黑樹性質5,每一個節點有相同黑高,若是將新插入節點的顏色設爲黑色,則樹會當即不知足紅黑樹性質,由於通過這個新插入節點的路徑其黑高都會比其它路徑多出一來,

  因此,要將新插入的節點顏色設爲紅色,可是對於根節點是個例外。另外,當新插入一個紅色節點時,紅黑樹性質的123均可以獲得維持,只有可能破壞性質4和性質5,如下分幾種狀況來分別討論新節點插入。

  (如下節點當其左右節點爲空時,都爲Nil節點,而且Nil節點算在節點的黑高裏面,此處爲了畫圖簡單並未將Nil節點畫)

  約定新插入節點爲N,N的父節點爲P,N的祖父節點爲G,N的叔節點爲U。

  1、樹爲空

    這是最簡單的狀況,直接將新插入節點標爲黑色便可

    

  2、父節點是黑色

    這是最簡單的狀況,這個時候,由於父節點是黑色,而新插入節點是紅色,全部新插入節點並無增長通過此節點的全部路徑黑高,依然知足紅黑樹全部性質,無需作修正處理。

    (N爲右節點時也是同樣的)

  3、父節點是紅色

    當父節點是紅色時,破壞了紅黑樹的性質4,父子節點不能同時爲紅色,也正由於性質4,因此祖父節點必定是黑色,否則在插入前不知足紅黑樹性質4.而叔節點顏色可紅可黑對於叔節點顏色按照紅和黑兩種狀況來分開  討論

  一、當叔節點顏色是紅色時(N是左節點或右節點都適合此狀況)

  

  按上圖所示,這個時候,須要將P,U和顏色和G的顏色交換,便可從局部上修正了性質4,並保持性質5,可是這個時候G的顏色由黑變紅,可能G的父節點也是紅色, 這個時候,就要把G節點按照處理N結點的狀況再處理一次。也就是向上傳遞修復。

  2.當U節點是黑色,且N是左節點時

  

  此時,先對G節點執行右旋轉操做(不熟悉旋轉操做的請看個人上一篇博客AVL樹,必定要很是熟悉旋轉操做才能理解紅黑樹),而後交換P,G節點的顏色,便可完成修復,本子樹根節點G修復前是黑色,修復後,新的子樹根節點P仍然是黑色,至此,修復完畢,無需向上傳遞。

  3.當U節點是黑色,且N是右節點時

  

  由以上由與前一種狀況相比,惟一的區別是N是右子節點,因此先對P節點執行左旋轉,便可轉化成前一種討論過的狀況,而後按前一種狀況來處理便可。

  好了,紅黑樹節點的全部狀況都在上面了,總的來看並不複雜,樹爲空和父節點爲黑色這兩種狀況很簡單,無需多言,至於下面的幾種狀況,總結起來就是,如何處理取決於U節點顏色

  爲紅時則交換PU和G的顏色,而後再對G處理。

  爲黑時分N爲左右節點情,爲左節點時右旋轉G,而後交換P與G的顏色,爲右子節點時先左旋轉P,而後再按左節點狀況進行處理。

  紅黑樹插入代碼以下:(代碼中的狀況一二三四五,以圖中的(1)(2)(3)(4)(5)對照)

 1 RBTreeNode* RBTreeInsert(RBTreeNode* pRoot, int nData) {
 2     if (pRoot == RBTreeNilNodePtr) {
 3         pRoot = MakeNewRBTreeNode(RBTreeNilNodePtr, nData);
 4         pRoot->isRed = RBTreeNodeBlack;
 5         return pRoot;
 6     }
 7     RBTreeNode* pCursor = pRoot;
 8     RBTreeNode* pParent = pRoot->pParent;
 9     while (pCursor != RBTreeNilNodePtr) {
10         pParent = pCursor;
11         if (nData > pCursor->nData) {
12             pCursor = pCursor->pRight;
13         }
14         else if (nData < pCursor->nData) {
15             pCursor = pCursor->pLeft;
16         }
17         else 
18             break;
19     }
20     if (pCursor == RBTreeNilNodePtr && pParent != RBTreeNilNodePtr) {
21         RBTreeNode* pNode = MakeNewRBTreeNode(pParent, nData);
22         if (nData > pParent->nData)
23             pParent->pRight = pNode;
24         else
25             pParent->pLeft = pNode;
26 
27         FixRBTreeNodeColor_Insert(&pRoot, pNode);
28     }
29     return pRoot;
30 }
31 
32 void FixRBTreeNodeColor_Insert(RBTreeNode** pRoot, RBTreeNode* pNode) {
33     if (!pNode->isRed)
34         return;
35 
36     RBTreeNode* pParent = pNode->pParent;
37     if (pParent == RBTreeNilNodePtr) {
38         //第一種狀況
39         pNode->isRed = RBTreeNodeBlack;
40         return;
41     }
42     else if (!pParent->isRed || pParent->pParent == RBTreeNilNodePtr)//pParent->pParent == RBTreeNilNodePtr爲第二種狀況
43         return;
44     RBTreeNode* pGrandParent = pParent->pParent;
45     if (pParent == pGrandParent->pLeft) {
46         RBTreeNode* pUncle = pGrandParent->pRight;
47         if (pUncle != RBTreeNilNodePtr) {
48             if (pUncle->isRed) {
49                 //狀況3:不須要旋轉操做,直接交換顏色,而後往上遞歸檢查 
50                 pParent->isRed = pUncle->isRed = RBTreeNodeBlack;
51                 pGrandParent->isRed = RBTreeNodeRed;
52                 return FixRBTreeNodeColor_Insert(pRoot, pGrandParent);
53             }
54         }
55         if (pNode == pParent->pRight) {
56             //狀況5,先左旋pParent轉換成狀況4
57             RBTreeLeftRotate(pRoot, pParent);
58         }
59         //狀況4
60         pGrandParent = RBTreeRightRotate(pRoot, pGrandParent);
61         SwapRBTreeNodeColor(pGrandParent, pGrandParent->pRight);
62     }
63     else {
64         //上面if分支的鏡像
65         RBTreeNode* pUncle = pGrandParent->pLeft;
66         if (pUncle != RBTreeNilNodePtr) {
67             if (pUncle->isRed) {
68                 //狀況3:不須要旋轉操做,直接交換顏色,而後往上遞歸檢查 
69                 pParent->isRed = pUncle->isRed = RBTreeNodeBlack;
70                 pGrandParent->isRed = RBTreeNodeRed;
71                 return FixRBTreeNodeColor_Insert(pRoot, pGrandParent);
72             }
73         }
74         if (pNode == pParent->pLeft) {
75             //狀況5,先右旋pParent轉換成狀況4
76             RBTreeRightRotate(pRoot, pParent);
77         }
78         //狀況4
79         pGrandParent = RBTreeLeftRotate(pRoot, pGrandParent);
80         SwapRBTreeNodeColor(pGrandParent, pGrandParent->pLeft);
81     }
82 }