红黑树(一):插入

  红黑树跟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 }