红黑树(一):插入

  红黑树跟AVL树一样,也是平衡二叉树,其查找、增加、删除效率为O(lgN),红黑树使用非常广泛,C++STL里面的map,set都是用红黑树实现的。

  以下就是一棵红黑树:

  

  《算法导论》上定义红黑树需满足以下五个性质:

  1.每个结点是黑色或是红色。

  2.根结点是黑色。

  3.所有叶子节点是黑色(也就是上面Nil结点)

     4.如果一个节点是红色的,则它的两个子结点都是黑色的。(也就是说父子节点不能同时是红色节点)

  5.对每个节点,从该结点到到叶子节点的路径上,均包含相同数目的黑色节点。

  需要说明一下的是,以上Nil结点是”哨兵节点“,将所有子节点为空的指针都以此Nil节点来代替,而且根节点的父节点也是Nil节点。对于第五条,任一节点到Nil节点的黑色结点个数(包含本身及Nil节点)都相同,也就是所谓的黑高相同。

  先来定义红黑树的节点,红黑树需要一个指示颜色的属性,用一个位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。

  一、树为空

    这是最简单的情况,直接将新插入节点标为黑色即可

    

  二、父节点是黑色

    这是最简单的情况,这个时候,因为父节点是黑色,而新插入节点是红色,所有新插入节点并没有增加经过此节点的所有路径黑高,依然满足红黑树所有性质,无需做修正处理。

    (N为右节点时也是一样的)

  三、父节点是红色

    当父节点是红色时,破坏了红黑树的性质4,父子节点不能同时为红色,也正因为性质4,所以祖父节点一定是黑色,不然在插入前不满足红黑树性质4.而叔节点颜色可红可黑对于叔节点颜色按照红和黑两种情况来分开  讨论

  1、当叔节点颜色是红色时(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 }

猜你喜欢

转载自www.cnblogs.com/zengyg/p/8975450.html