紅黑樹跟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 }