平衡二叉查找树:红黑树
红黑树的定义
红黑树是满足如下条件的二叉树:
(1)每个结点都有颜色标记,要么是黑色,要么是红色
(2)根结点是黑色的
(3)叶子结点是黑色的(按《算法导论》和其他文献的说法是,这里的叶子结点指的是空结点)
(4)红色结点的孩子必须是黑色的
(5)从根结点到每一个叶子结点的路径上,黑色结点的个数相同。(有部分文献里“根结点”说成“任意结点”,其实这种说法是一样的,因为任意子树也是一颗二叉树,如果它的根是黑色的,那么它本身也是一颗红黑树,如果它的根是红色的,由于它属于红黑树的一部分,而它的对于“黑色结点的个数”没有贡献,因此它到它的叶子结点路径上的黑色个数也必然是相同的)
注:以上5个性质在插入、删除的过程是时刻要维护的。
红黑树比朴素二叉查找树效率更高的原因
红黑树的查找、插入、删除操作都可以在最坏情况下保持为O(log2N),其中N为树的结点个数。而朴素二叉查找树则在最坏情况下为O(N)。其实,红黑树并非严格意义上的高平衡度的平衡二叉树,它之所以能够维持高效,是根据它的定义内容来实现的。假设红黑树的高为H,结点数为N,则根据(4)可以知道,在一颗红黑树中,黑色结点的高度至少为H/2,则可以得到这样的一个不等式:
2H/2 - 1 <= N(高度为H/2的二叉树中结点个树最多为 2H/2 - 1)
原公式可以化为H <= 2*log2(N+1),从而可以知道红黑树的高度最多为近似log2N的两倍。忽略常数,红黑树的所有操作便是O(log2N)
二叉树中的旋转操作
旋转操作是调整二叉树结构的一种方法,通过旋转,该二叉树的中序遍历序列不变。主要有两个方向的旋转。用圆形代表结点,矩形代表一颗子树。
右旋:
根据图可以看出,右旋其实就是把B的右孩子作为A的左孩子,再将B作为根,其右孩子为A。
左旋:
左旋其实就是右旋的镜像,把B的左孩子作为A的右孩子,将B作为根,其左孩子为A。
注:红黑树维持其结构性质的过程中,需要用到上述旋转操作。
红黑树的插入操作
红黑树的插入跟普通二叉查找树的插入是一样的,但是插入过程中很容易就会破坏红黑树的结构。维护红黑树的结构就是要保证其5个性质一直得以保持。
首先,规定新插入的结点的颜色是红色的。为什么呢?因为如果插入一个黑色结点,那么性质(5)瞬间被破坏。接下来,就会遇到以下几种情况:
1.原来的树是一颗空树,则直接把新插入的结点的颜色改为黑色
2.新插入结点的父结点是黑色,无需任何操作。(因为新插入一个红色结点在这种情况下不破坏红黑树的任何性质)
3.新插入结点的父结点是红色,破坏了性质(4)
第1,2种情况都是很乐观的,主要问题就在于第3种情况,解决这种情况需要考虑到新插入结点的叔叔结点。首先设新插入的结点为x, 其父亲为px, 叔叔为u。假定px是A的左孩子来分析问题。(px是A的右孩子的情况是类似的,只是某些操作是反过来而已(旋转))。
Case 1:如果叔叔u为红色
直接把新插入结点x的父亲px和叔叔u染成黑色,并将x的祖父A染成红色。其实这种情况可以这样理解,红黑树中性质(5)的保持,A是做了贡献的,它贡献了一个黑色。这个操作就相当于把它的黑色往下“推”了。至于为什么A一定是黑色?因为u是红色,根据性质(4),A不可能是红色。接下来,由于A变成了红色,那么如果A的父亲也是红色呢?所以插入是一个循环的过程,接下来就要查看A的情况了,这时它就相当于最初的x了。
Case 2:如果叔叔u是黑色的,并且x是px的左孩子
由于u是黑色的,它是对性质(5)有贡献的,按照原来“下推”的操作已经没办法完成了。因为这样的话(root->...->A->u->...)这条路径就少了一个黑色。所以,这种情况下的解决办法是把新插入结点x的父亲px染成黑色,把它的祖父A染成红色,然后对A进行一次右旋。对于这种情况,其实就相当于把px的红色转到右子树去了,又因为u是黑色,所以把红色转过去不会影响性质(4)。
Case 3:如果叔叔u是黑色的,并且x是px的右孩子
这种情况首先对新插入结点x的父亲px进行一次左旋。然后得到的结果正好跟Case 2一样了。
红黑树的插入操作就几种,对于px是其父亲A的右孩子的情况,则如上述所言,是镜像的。
代码如下:
1 template <typename T> void RedBlackTree <T>::insert(const T &data)
2 {
3 RBTreeNode <T> *pNewNode = new RBTreeNode <T>(data);
4 RBTreeNode <T> *pNode = pRoot, *pPreNode = NULL;
5 while (pNode != NULL)
6 {
7 pPreNode = pNode;
8 if (pNode->m_nValue <= data)
9 pNode = pNode->m_pRight;
10 else
11 pNode = pNode->m_pLeft;
12 }
13 if (pPreNode == NULL)
14 {
15 pRoot = pNewNode;
16 }
17 else
18 {
19 if (pPreNode->m_nValue <= data)
20 pPreNode->m_pRight = pNewNode;
21 else
22 pPreNode->m_pLeft = pNewNode;
23 pNewNode->m_pParent = pPreNode;
24 }
25 insert_fixed(pNewNode);
26 total_nodes++;
27 }
28
29 template <typename T> void RedBlackTree <T>::insert_fixed(RBTreeNode <T> *&root)
30 {
31 if (root == NULL)
32 return;
33 RBTreeNode <T> *pParent = NULL;
34 while (root->color == red) //如果当前结点是红色的,可能其父亲也是红色的
35 {
36 pParent = root->m_pParent;
37 if (pParent == NULL || pParent->color != red || pParent->m_pParent == NULL) //父亲不是红色,结束
38 break;
39 RBTreeNode <T> *pGrandparent = pParent->m_pParent;
40 RBTreeNode <T> *pUncle = NULL;
41 if (pGrandparent->m_pLeft == pParent)
42 pUncle = pGrandparent->m_pRight;
43 else if (pGrandparent->m_pRight == pParent)
44 pUncle = pGrandparent->m_pLeft;
45 if (pUncle != NULL && pUncle->color == red) //叔叔结点是红色的
46 {
47 pParent->color = black;
48 pUncle->color = black;
49 pGrandparent->color = red;
50 }
51 else //叔叔结点为空或者为黑色
52 {
53 if (pParent == pGrandparent->m_pLeft)
54 {
55 if (root == pParent->m_pRight)
56 rotate_left(pParent);
57 pParent->color = black;
58 pGrandparent->color = red;
59 rotate_right(pGrandparent);
60 }
61 else if (pParent == pGrandparent->m_pRight)
62 {
63 if (root == pParent->m_pLeft)
64 rotate_right(pParent);
65 pParent->color = black;
66 pGrandparent->color = red;
67 rotate_left(pGrandparent);
68 }
69 }
70 root = pGrandparent; //往树上爬,看是否还有违反性质的结点
71 }
72 pRoot->color = black;
73 }
<h1 style="margin: 0px; padding: 0px; font-size: 28px; color: rgb(68, 68, 68); font-family: tahoma, arial, sans-serif; background-color: rgb(255, 255, 255);"><span style="margin: 0px; padding: 0px; color: rgb(51, 102, 255); font-size: 15px;">红黑树的删除操作</span></h1><p style="margin: 10px auto; padding-top: 0px; padding-bottom: 0px; line-height: 18px; color: rgb(68, 68, 68); font-family: tahoma, arial, sans-serif; background-color: rgb(255, 255, 255);"><span style="margin: 0px; padding: 0px; font-size: 15px;"> 这部分就麻烦多了。但是作为真男人,有困难要上,没困难,制造困难也要上......</span></p><p style="margin: 10px auto; padding-top: 0px; padding-bottom: 0px; line-height: 18px; color: rgb(68, 68, 68); font-family: tahoma, arial, sans-serif; background-color: rgb(255, 255, 255);"><span style="margin: 0px; padding: 0px; font-size: 15px;"> 首先,红黑树是二叉树,它的删除跟普通二叉查找树如出一辙,问题还是在于维护它的结构。最理想的情况是,被删除的结点是一个红色结点,那么万事大吉,直接删掉就可以了,其他什么事都不用干。因为它不会影响上述(1)~(5)所有性质。</span></p><p style="margin: 10px auto; padding-top: 0px; padding-bottom: 0px; line-height: 18px; color: rgb(68, 68, 68); font-family: tahoma, arial, sans-serif; background-color: rgb(255, 255, 255);"><span style="margin: 0px; padding: 0px; font-size: 15px;"> 如果删除的是黑色结点,那么就有如下几种情况了(这里也以被删除的结点都是其父亲的左孩子为例子,相反情况则镜像):</span></p><p style="margin: 10px auto; padding-top: 0px; padding-bottom: 0px; line-height: 18px; color: rgb(68, 68, 68); font-family: tahoma, arial, sans-serif; background-color: rgb(255, 255, 255);"><span style="margin: 0px; padding: 0px; font-size: 15px;">1.删除一个结点后,必然是它的一个孩子(可能为NULL)替补上来,如果这个孩子是红色的话,那就好办了,直接把这个替补结点染成黑色即可。</span></p><p style="margin: 10px auto; padding-top: 0px; padding-bottom: 0px; line-height: 18px; color: rgb(68, 68, 68); font-family: tahoma, arial, sans-serif; background-color: rgb(255, 255, 255);"><span style="margin: 0px; padding: 0px; font-size: 15px;">首先看看删除的结点只有一个孩子的情况:</span></p><p style="margin: 10px auto; padding-top: 0px; padding-bottom: 0px; line-height: 18px; color: rgb(68, 68, 68); font-family: tahoma, arial, sans-serif; background-color: rgb(255, 255, 255);"><span style="margin: 0px; padding: 0px; font-size: 15px;"><img src="http://images.cnitblog.com/i/471144/201405/230048089966632.jpg" alt="" style="margin: 0px; padding: 0px; border: 0px;" /></span></p><p style="margin: 10px auto; padding-top: 0px; padding-bottom: 0px; line-height: 18px; color: rgb(68, 68, 68); font-family: tahoma, arial, sans-serif; background-color: rgb(255, 255, 255);"><span style="margin: 0px; padding: 0px; font-size: 15px;">这里d不管是被删除的结点a的左孩子还是右孩子,d都是替换a的,d最后成为了rt的左孩子。</span></p><p style="margin: 10px auto; padding-top: 0px; padding-bottom: 0px; line-height: 18px; color: rgb(68, 68, 68); font-family: tahoma, arial, sans-serif; background-color: rgb(255, 255, 255);"><span style="margin: 0px; padding: 0px; font-size: 15px;">如果被删除的结点有两个孩子的话:</span></p><p style="margin: 10px auto; padding-top: 0px; padding-bottom: 0px; line-height: 18px; color: rgb(68, 68, 68); font-family: tahoma, arial, sans-serif; background-color: rgb(255, 255, 255);"><span style="margin: 0px; padding: 0px; font-size: 15px;"><img src="http://images.cnitblog.com/i/471144/201405/230059143714323.jpg" alt="" style="margin: 0px; padding: 0px; border: 0px;" /></span></p><p style="margin: 10px auto; padding-top: 0px; padding-bottom: 0px; line-height: 18px; color: rgb(68, 68, 68); font-family: tahoma, arial, sans-serif; background-color: rgb(255, 255, 255);"><span style="margin: 0px; padding: 0px; font-size: 15px;">这里要删除的结点是a,跟普通二叉查找树的删除一样,找到其后继f,f如果是后继,那么它必然无左子树。然后,把h替换到f这个位置,而把f替换到a这个位置。因为h是红色,直接染成黑色就可以了。</span></p><p style="margin: 10px auto; padding-top: 0px; padding-bottom: 0px; line-height: 18px; color: rgb(68, 68, 68); font-family: tahoma, arial, sans-serif; background-color: rgb(255, 255, 255);"><span style="margin: 0px; padding: 0px; font-size: 15px;">2.如果替补上来的结点也是黑色,这就是最麻烦的地方了。因为被删除的结点是黑色(假定为a结点),这必然导致它的某个祖先违反了性质(5),因为从该祖先到a的所有子树上的路径的黑色结点数都少了1,而替补上来的又恰好是黑的,没办法填补这个失去的一个黑色了。为了保持性质(5)不被破坏,《算法导论》里说把替补上来的结点当成有两重黑色,这样便填补了那个失去的一个黑色,但问题在于,这样的话替补结点违反了性质(1)!每个结点只能是黑色或红色,而这个替补结点却是双重黑色。于是,问题就在于如何把替补结点的双重黑色去掉一重,从而保持性质(1)不被破坏。</span></p><p style="margin: 10px auto; padding-top: 0px; padding-bottom: 0px; line-height: 18px; color: rgb(68, 68, 68); font-family: tahoma, arial, sans-serif; background-color: rgb(255, 255, 255);"><span style="margin: 0px; padding: 0px; font-size: 15px;">这里的解决方法按四步走,需要注意的是,删除后重新维护红黑树结构的四个步骤并不是一步到位的!而是通过重复走这四个步骤中的某几个,最终把性质(1)保持下来。</span></p><p style="margin: 10px auto; padding-top: 0px; padding-bottom: 0px; line-height: 18px; color: rgb(68, 68, 68); font-family: tahoma, arial, sans-serif; background-color: rgb(255, 255, 255);"><span style="margin: 0px; padding: 0px; font-size: 15px;"><span style="margin: 0px; padding: 0px;">Step 1:替补结点的兄弟是红色的</span></span></p><p style="margin: 10px auto; padding-top: 0px; padding-bottom: 0px; line-height: 18px; color: rgb(68, 68, 68); font-family: tahoma, arial, sans-serif; background-color: rgb(255, 255, 255);"><span style="margin: 0px; padding: 0px; font-size: 15px;"><img src="http://images.cnitblog.com/i/471144/201405/230122433716732.jpg" alt="" width="697" height="175" style="margin: 0px; padding: 0px; border: 0px;" /></span></p><p style="margin: 10px auto; padding-top: 0px; padding-bottom: 0px; line-height: 18px; color: rgb(68, 68, 68); font-family: tahoma, arial, sans-serif; background-color: rgb(255, 255, 255);"><span style="margin: 0px; padding: 0px; font-size: 15px;">这里要删除的结点是a,其后继为f,而替补f的h是黑色的。通过替换操作后,得到第二个图。h是双重黑色的,而它的兄弟g是红色的。这时的操作是,把g染成黑色,把e染成红色,得到第三个图。最后,将e进行一次左旋得到第四个图。需要注意的是,这时并不是说性质(1)就得到保持了,此时h的双重黑色还在,接下来的操作就得看结点 i 颜色了。如果结点 i 是红色,那么进行的还是Step 1,如果 i 是黑色,则进行其他步骤。</span></p><p style="margin: 10px auto; padding-top: 0px; padding-bottom: 0px; line-height: 18px; color: rgb(68, 68, 68); font-family: tahoma, arial, sans-serif; background-color: rgb(255, 255, 255);"><span style="margin: 0px; padding: 0px; font-size: 15px;"><span style="margin: 0px; padding: 0px;">Step 2:替补结点的兄弟是黑色的,并且其兄弟结点的两个孩子都是黑色的</span></span></p><p style="margin: 10px auto; padding-top: 0px; padding-bottom: 0px; line-height: 18px; color: rgb(68, 68, 68); font-family: tahoma, arial, sans-serif; background-color: rgb(255, 255, 255);"><span style="margin: 0px; padding: 0px; font-size: 15px;"><img src="http://images.cnitblog.com/i/471144/201405/230133035744457.jpg" alt="" width="678" height="173" style="margin: 0px; padding: 0px; border: 0px;" /></span></p><p style="margin: 10px auto; padding-top: 0px; padding-bottom: 0px; line-height: 18px; color: rgb(68, 68, 68); font-family: tahoma, arial, sans-serif; background-color: rgb(255, 255, 255);"><span style="margin: 0px; padding: 0px; font-size: 15px;">同样,经过替换后得到第二个图。此时h的兄弟g为黑色,并且g有两个黑色的孩子,这时把g染成红色,并把h那多余的一重黑色上升到e上面,如果e为红色,直接把e染成黑色就大功告成了。上述图示里e就恰好是红色。那如果e是黑色呢?此时e变成双重黑色了,接下来的操作,就看e的兄弟是什么状况了,然后再根据状况去选择步骤。</span></p><p style="margin: 10px auto; padding-top: 0px; padding-bottom: 0px; line-height: 18px; color: rgb(68, 68, 68); font-family: tahoma, arial, sans-serif; background-color: rgb(255, 255, 255);"><span style="margin: 0px; padding: 0px; font-size: 15px;"><span style="margin: 0px; padding: 0px;">Step 3:替补结点的兄弟是黑色的,且其左孩子结点是红色的,右孩子的结点是黑色的。</span></span></p><p style="margin: 10px auto; padding-top: 0px; padding-bottom: 0px; line-height: 18px; color: rgb(68, 68, 68); font-family: tahoma, arial, sans-serif; background-color: rgb(255, 255, 255);"><span style="margin: 0px; padding: 0px; font-size: 15px;"><img src="http://images.cnitblog.com/i/471144/201405/230149402936587.jpg" alt="" width="658" height="160" style="margin: 0px; padding: 0px; border: 0px;" /></span></p><p style="margin: 10px auto; padding-top: 0px; padding-bottom: 0px; line-height: 18px; color: rgb(68, 68, 68); font-family: tahoma, arial, sans-serif; background-color: rgb(255, 255, 255);"><span style="margin: 0px; padding: 0px; font-size: 15px;">照旧,先替换得到第二个图,这是h是双重黑色的,其兄弟g为黑色,且有一个红色左孩子 i 和黑色左孩子 j,这时把 i 染成黑色,并把兄弟g染成红色得到第三个图。对兄弟g进行右旋得到第四个图。此时,h的兄弟是 i 了,把 i 染成其父亲e的颜色,并把其父亲e和右孩子g染成黑色得到第五个图。最后对e进行一次左旋,得到第六个图。</span></p><p style="margin: 10px auto; padding-top: 0px; padding-bottom: 0px; line-height: 18px; color: rgb(68, 68, 68); font-family: tahoma, arial, sans-serif; background-color: rgb(255, 255, 255);"><span style="margin: 0px; padding: 0px; font-size: 15px;"><span style="margin: 0px; padding: 0px;">Step 4:替补结点的兄弟是黑色的,且其右孩子结点是红色的。</span></span></p><p style="margin: 10px auto; padding-top: 0px; padding-bottom: 0px; line-height: 18px; color: rgb(68, 68, 68); font-family: tahoma, arial, sans-serif; background-color: rgb(255, 255, 255);"><span style="margin: 0px; padding: 0px; font-size: 15px;"><img src="http://images.cnitblog.com/i/471144/201405/230157557468290.jpg" alt="" width="638" height="182" style="margin: 0px; padding: 0px; border: 0px;" /></span></p><p style="margin: 10px auto; padding-top: 0px; padding-bottom: 0px; line-height: 18px; color: rgb(68, 68, 68); font-family: tahoma, arial, sans-serif; background-color: rgb(255, 255, 255);"><span style="margin: 0px; padding: 0px; font-size: 15px;">替换得到的第二个图恰好跟 Step 3里的第四个图是一样的。其实这不是恰好,而是Step 3目的就是为了转换成Step 4的。到了Step 4,所以转换都完成了,因为h多余的那层黑色被转换到了其兄弟那颗树中去了(原本红色的 j 变成了黑色)。</span></p><p style="margin: 10px auto; padding-top: 0px; padding-bottom: 0px; line-height: 18px; color: rgb(68, 68, 68); font-family: tahoma, arial, sans-serif; background-color: rgb(255, 255, 255);"><span style="margin: 0px; padding: 0px; font-size: 15px;"> 至此,删除的所有操作都结束了。需要注意的是,以上所有的“左”,“右”都是以“被删除结点是其父亲的左孩子”为例子而言的,如果不是,则恰好相反。</span></p><p style="margin: 10px auto; padding-top: 0px; padding-bottom: 0px; line-height: 18px; color: rgb(68, 68, 68); font-family: tahoma, arial, sans-serif; background-color: rgb(255, 255, 255);"><span style="margin: 0px; padding: 0px; font-size: 15px;">代码如下:</span></p><pre name="code" class="cpp"> 1 template <typename T> void RedBlackTree <T>::remove(const T &data)
2 {
3 if (pRoot == NULL)
4 return;
5 RBTreeNode <T> *pNode = pRoot;
6 while (pNode != NULL)
7 {
8 if (pNode->m_nValue == data)
9 break;
10 else if (pNode->m_nValue < data)
11 pNode = pNode->m_pRight;
12 else
13 pNode = pNode->m_pLeft;
14 }
15 if (pNode == NULL) //树中没有该结点
16 return;
17 Color delete_color; //被删除结点的颜色
18 Direction direction; //替补结点是其父亲的哪个子树,左或右
19 RBTreeNode <T> *pParent = NULL;
20 if (pNode->m_pLeft != NULL && pNode->m_pRight != NULL) //删除的结点有两个孩子,需要找到后继
21 {
22 RBTreeNode <T> *pSuccessor = pNode->m_pRight;
23 while (pSuccessor->m_pLeft != NULL)
24 pSuccessor = pSuccessor->m_pLeft;
25 pParent = pSuccessor->m_pParent;
26 if (pParent->m_pLeft == pSuccessor)
27 {
28 pParent->m_pLeft = pSuccessor->m_pRight;
29 direction = _left;
30 }
31 else if (pParent->m_pRight == pSuccessor)
32 {
33 pParent->m_pRight = pSuccessor->m_pRight;
34 direction = _right;
35 }
36 if (pSuccessor->m_pRight != NULL)
37 pSuccessor->m_pRight->m_pParent = pParent;
38 pNode->m_nValue = pSuccessor->m_nValue;
39 delete_color = pSuccessor->color;
40 delete pSuccessor;
41 pSuccessor = NULL;
42 }
43 else if (pNode->m_pLeft != NULL) //删除的结点只有左孩子
44 {
45 RBTreeNode <T> *pChild = pNode->m_pLeft;
46 pParent = pNode->m_pParent;
47 pNode->m_nValue = pChild->m_nValue;
48 pNode->m_pLeft = NULL;
49 delete_color = pChild->color;
50 if (pParent != NULL)
51 {
52 if (pParent->m_pLeft == pNode)
53 direction = _left;
54 else if (pParent->m_pRight == pNode)
55 direction = _right;
56 }
57 delete pChild;
58 pChild = NULL;
59 }
60 else if (pNode->m_pRight != NULL) //删除的结点只有右孩子
61 {
62 RBTreeNode <T> *pChild = pNode->m_pRight;
63 pParent = pNode->m_pParent;
64 pNode->m_nValue = pChild->m_nValue;
65 pNode->m_pRight = NULL;
66 delete_color = pChild->color;
67 if (pParent != NULL)
68 {
69 if (pParent->m_pLeft == pNode)
70 direction = _left;
71 else if (pParent->m_pRight == pNode)
72 direction = _right;
73 }
74 delete pChild;
75 pChild = NULL;
76 }
77 else
78 {
79 pParent = pNode->m_pParent;
80 delete_color = pNode->color;
81 if (pParent != NULL)
82 {
83 if (pParent->m_pLeft == pNode)
84 direction = _left;
85 else if (pParent->m_pRight == pNode)
86 direction = _right;
87 }
88 if (pParent->m_pLeft == pNode)
89 pParent->m_pLeft = NULL;
90 else if (pParent->m_pRight == pNode)
91 pParent->m_pRight = NULL;
92 delete pNode;
93 pNode = NULL;
94 }
95 if (pParent == NULL)
96 pRoot->color = black;
97 else if (delete_color == black)
98 remove_fixed(pParent, direction);
99 total_nodes--;
100 }
101
102 template <typename T> void RedBlackTree <T>::remove_fixed(RBTreeNode <T> *&root, Direction direction)
103 {
104 if (root == NULL)
105 return;
106 RBTreeNode <T> *pNode = direction == _left ? root->m_pLeft : root->m_pRight;
107 while (pNode != pRoot)
108 {
109 if (pNode != NULL && pNode->color == red)
110 break;
111 RBTreeNode <T> *pBrother = direction == _left ? root->m_pRight : root->m_pLeft;
112 if (pBrother != NULL && pBrother->color == red)
113 {
114 pBrother->color = black;
115 root->color = red;
116 if (direction == _left)
117 {
118 rotate_left(root);
119 root = root->m_pLeft;
120 if (root != NULL)
121 {
122 pNode = root->m_pLeft;
123 pBrother = root->m_pRight;
124 }
125 }
126 else if (direction == _right)
127 {
128 rotate_right(root);
129 root = root->m_pRight;
130 if (root != NULL)
131 {
132 pNode = root->m_pRight;
133 pBrother = root->m_pLeft;
134 }
135 }
136 }
137 if (pBrother == NULL)
138 {
139 pNode = root;
140 if (root != NULL)
141 {
142 root = root->m_pParent;
143 if (root != NULL)
144 {
145 if (root->m_pLeft == pNode)
146 direction = _left;
147 else if (root->m_pRight == pNode)
148 direction = _right;
149 }
150 }
151 }
152 else if ((pBrother->m_pLeft == NULL || pBrother->m_pLeft->color == black) &&
153 (pBrother->m_pRight == NULL || pBrother->m_pRight->color == black))
154 {
155
156 pBrother->color = red;
157 pNode = root;
158 if (root != NULL)
159 {
160 root = root->m_pParent;
161 if (root != NULL)
162 {
163 if (root->m_pLeft == pNode)
164 direction = _left;
165 else if (root->m_pRight == pNode)
166 direction = _right;
167 }
168 }
169 }
170
171 else if (pBrother != NULL)
172 {
173 if (pBrother->m_pRight == NULL || pBrother->m_pRight->color == black)
174 {
175 if (pBrother->m_pLeft != NULL)
176 pBrother->m_pLeft->color = black;
177 pBrother->color = red;
178 if (direction == _left)
179 rotate_right(pBrother);
180 else if (direction == _right)
181 rotate_left(pBrother);
182 }
183 pBrother->color = root->color;
184 root->color = black;
185 if (direction == _left)
186 {
187 rotate_left(root);
188 if (pBrother->m_pRight != NULL)
189 pBrother->m_pRight->color = black;
190 }
191 else if (direction == _right)
192 {
193 rotate_right(root);
194 if (pBrother->m_pLeft != NULL)
195 pBrother->m_pLeft->color = black;
196 }
197 pNode = pRoot;
198 }
199 }
200 pNode->color = black;
201 }
完整的代码如下,由于没有采用《算法导论》里的“哨兵”方法,代码中多了很多判断语句,显得冗长。改日再改成“哨兵”的方法。
1 #include <iostream>
2 #include <stdexcept>
3
4 using namespace std;
5
6 //枚举类型,分别是结点的颜色
7 enum Color
8 {
9 red, black
10 };
11 //红黑树结点定义
12 template <typename T>
13 class RBTreeNode
14 {
15 public:
16 RBTreeNode();
17 RBTreeNode(const T &);
18 RBTreeNode(const RBTreeNode &);
19 public:
20 T m_nValue;
21 Color color;
22 RBTreeNode *m_pParent;
23 RBTreeNode *m_pLeft;
24 RBTreeNode *m_pRight;
25 };
26
27 template <typename T> RBTreeNode <T>::RBTreeNode()
28 {
29 m_pParent = NULL;
30 m_pLeft = NULL;
31 m_pRight = NULL;
32 }
33 template <typename T> RBTreeNode <T>::RBTreeNode(const T &rhs)
34 {
35 m_nValue = rhs;
36 color = red;
37 m_pParent = NULL;
38 m_pLeft = NULL;
39 m_pRight = NULL;
40 }
41 template <typename T> RBTreeNode <T>::RBTreeNode(const RBTreeNode <T> &rhs)
42 {
43 m_nValue = rhs.m_nValue;
44 color = rhs.color;
45 m_pParent = rhs.m_pParent;
46 m_pLeft = rhs.m_pLeft;
47 m_pRight = rhs.m_pRight;
48 }
49
50 //-----------------------------分割线-----------------------------
51
52 enum Direction
53 {
54 _left, _right
55 };
56 //红黑树的操作定义
57 template <typename T>
58 class RedBlackTree
59 {
60 public:
61 RedBlackTree();
62 ~RedBlackTree();
63 bool empty();
64 size_t size();
65 void insert(const T &);
66 void remove(const T &);
67 bool search(const T &);
68 T maximun();
69 T minimun();
70 private:
71 void insert_fixed(RBTreeNode <T> *&);
72 void remove_fixed(RBTreeNode <T> *&, Direction);
73 void rotate_left(RBTreeNode <T> *&);
74 void rotate_right(RBTreeNode <T> *&);
75 void delete_all(RBTreeNode <T> *&);
76 private:
77 //把复制构造函数和赋值操作符声明为private且不定义,目的是拒绝编译器生成的版本。意思就是树不能被复制。
78 RedBlackTree(const RedBlackTree <T> &);
79 RedBlackTree & operator= (const RedBlackTree <T> &);
80 private:
81 RBTreeNode <T> *pRoot;
82 size_t total_nodes;
83 };
84
85 template <typename T> RedBlackTree <T>::RedBlackTree()
86 {
87 pRoot = NULL;
88 total_nodes = 0;
89 }
90 template <typename T> RedBlackTree <T>::~RedBlackTree()
91 {
92 delete_all(pRoot);
93 total_nodes = 0;
94 }
95 template <typename T> bool RedBlackTree <T>::empty()
96 {
97 return total_nodes == 0 ? true : false;
98 }
99 template <typename T> size_t RedBlackTree <T>::size()
100 {
101 return total_nodes;
102 }
103 template <typename T> void RedBlackTree <T>::insert(const T &data)
104 {
105 RBTreeNode <T> *pNewNode = new RBTreeNode <T>(data);
106 RBTreeNode <T> *pNode = pRoot, *pPreNode = NULL;
107 while (pNode != NULL)
108 {
109 pPreNode = pNode;
110 if (pNode->m_nValue <= data)
111 pNode = pNode->m_pRight;
112 else
113 pNode = pNode->m_pLeft;
114 }
115 if (pPreNode == NULL)
116 {
117 pRoot = pNewNode;
118 }
119 else
120 {
121 if (pPreNode->m_nValue <= data)
122 pPreNode->m_pRight = pNewNode;
123 else
124 pPreNode->m_pLeft = pNewNode;
125 pNewNode->m_pParent = pPreNode;
126 }
127 insert_fixed(pNewNode);
128 total_nodes++;
129 }
130 template <typename T> void RedBlackTree <T>::remove(const T &data)
131 {
132 if (pRoot == NULL)
133 return;
134 RBTreeNode <T> *pNode = pRoot;
135 while (pNode != NULL)
136 {
137 if (pNode->m_nValue == data)
138 break;
139 else if (pNode->m_nValue < data)
140 pNode = pNode->m_pRight;
141 else
142 pNode = pNode->m_pLeft;
143 }
144 if (pNode == NULL) //树中没有该结点
145 return;
146 Color delete_color; //被删除结点的颜色
147 Direction direction; //替补结点是其父亲的哪个子树,左或右
148 RBTreeNode <T> *pParent = NULL;
149 if (pNode->m_pLeft != NULL && pNode->m_pRight != NULL) //删除的结点有两个孩子,需要找到后继
150 {
151 RBTreeNode <T> *pSuccessor = pNode->m_pRight;
152 while (pSuccessor->m_pLeft != NULL)
153 pSuccessor = pSuccessor->m_pLeft;
154 pParent = pSuccessor->m_pParent;
155 if (pParent->m_pLeft == pSuccessor)
156 {
157 pParent->m_pLeft = pSuccessor->m_pRight;
158 direction = _left;
159 }
160 else if (pParent->m_pRight == pSuccessor)
161 {
162 pParent->m_pRight = pSuccessor->m_pRight;
163 direction = _right;
164 }
165 if (pSuccessor->m_pRight != NULL)
166 pSuccessor->m_pRight->m_pParent = pParent;
167 pNode->m_nValue = pSuccessor->m_nValue;
168 delete_color = pSuccessor->color;
169 delete pSuccessor;
170 pSuccessor = NULL;
171 }
172 else if (pNode->m_pLeft != NULL) //删除的结点只有左孩子
173 {
174 RBTreeNode <T> *pChild = pNode->m_pLeft;
175 pParent = pNode->m_pParent;
176 pNode->m_nValue = pChild->m_nValue;
177 pNode->m_pLeft = NULL;
178 delete_color = pChild->color;
179 if (pParent != NULL)
180 {
181 if (pParent->m_pLeft == pNode)
182 direction = _left;
183 else if (pParent->m_pRight == pNode)
184 direction = _right;
185 }
186 delete pChild;
187 pChild = NULL;
188 }
189 else if (pNode->m_pRight != NULL) //删除的结点只有右孩子
190 {
191 RBTreeNode <T> *pChild = pNode->m_pRight;
192 pParent = pNode->m_pParent;
193 pNode->m_nValue = pChild->m_nValue;
194 pNode->m_pRight = NULL;
195 delete_color = pChild->color;
196 if (pParent != NULL)
197 {
198 if (pParent->m_pLeft == pNode)
199 direction = _left;
200 else if (pParent->m_pRight == pNode)
201 direction = _right;
202 }
203 delete pChild;
204 pChild = NULL;
205 }
206 else
207 {
208 pParent = pNode->m_pParent;
209 delete_color = pNode->color;
210 if (pParent != NULL)
211 {
212 if (pParent->m_pLeft == pNode)
213 direction = _left;
214 else if (pParent->m_pRight == pNode)
215 direction = _right;
216 }
217 if (pParent->m_pLeft == pNode)
218 pParent->m_pLeft = NULL;
219 else if (pParent->m_pRight == pNode)
220 pParent->m_pRight = NULL;
221 delete pNode;
222 pNode = NULL;
223 }
224 if (pParent == NULL)
225 pRoot->color = black;
226 else if (delete_color == black)
227 remove_fixed(pParent, direction);
228 total_nodes--;
229 }
230 template <typename T> bool RedBlackTree <T>::search(const T &data)
231 {
232 if (pRoot == NULL)
233 return false;
234 RBTreeNode <T> *pNode = pRoot;
235 while (pNode != NULL)
236 {
237 if (pNode->m_nValue == data)
238 return true;
239 else if (pNode->m_nValue <= data)
240 pNode = pNode->m_pRight;
241 else if (pNode->m_nValue > data)
242 pNode = pNode->m_pLeft;
243 }
244 return false;
245 }
246 template <typename T> T RedBlackTree <T>::maximun()
247 {
248 if (pRoot == NULL)
249 throw range_error("The Red-Black Tree is empty");
250 RBTreeNode <T> *pNode = pRoot;
251 while (pNode->m_pRight != NULL)
252 pNode = pNode->m_pRight;
253 return pNode->m_nValue;
254 }
255 template <typename T> T RedBlackTree <T>::minimun()
256 {
257 if (pRoot == NULL)
258 throw range_error("The Red-Black Tree is empty");
259 RBTreeNode <T> *pNode = pRoot;
260 while (pNode->m_pLeft != NULL)
261 pNode = pNode->m_pLeft;
262 return pNode->m_nValue;
263 }
264 template <typename T> void RedBlackTree <T>::insert_fixed(RBTreeNode <T> *&root)
265 {
266 if (root == NULL)
267 return;
268 RBTreeNode <T> *pParent = NULL;
269 while (root->color == red) //如果当前结点是红色的,可能其父亲也是红色的
270 {
271 pParent = root->m_pParent;
272 if (pParent == NULL || pParent->color != red || pParent->m_pParent == NULL) //父亲不是红色,结束
273 break;
274 RBTreeNode <T> *pGrandparent = pParent->m_pParent;
275 RBTreeNode <T> *pUncle = NULL;
276 if (pGrandparent->m_pLeft == pParent)
277 pUncle = pGrandparent->m_pRight;
278 else if (pGrandparent->m_pRight == pParent)
279 pUncle = pGrandparent->m_pLeft;
280 if (pUncle != NULL && pUncle->color == red) //叔叔结点是红色的
281 {
282 pParent->color = black;
283 pUncle->color = black;
284 pGrandparent->color = red;
285 }
286 else //叔叔结点为空或者为黑色
287 {
288 if (pParent == pGrandparent->m_pLeft)
289 {
290 if (root == pParent->m_pRight)
291 rotate_left(pParent);
292 pParent->color = black;
293 pGrandparent->color = red;
294 rotate_right(pGrandparent);
295 }
296 else if (pParent == pGrandparent->m_pRight)
297 {
298 if (root == pParent->m_pLeft)
299 rotate_right(pParent);
300 pParent->color = black;
301 pGrandparent->color = red;
302 rotate_left(pGrandparent);
303 }
304 }
305 root = pGrandparent; //往树上爬,看是否还有违反性质的结点
306 }
307 pRoot->color = black;
308 }
309 template <typename T> void RedBlackTree <T>::remove_fixed(RBTreeNode <T> *&root, Direction direction)
310 {
311 if (root == NULL)
312 return;
313 RBTreeNode <T> *pNode = direction == _left ? root->m_pLeft : root->m_pRight;
314 while (pNode != pRoot)
315 {
316 if (pNode != NULL && pNode->color == red)
317 break;
318 RBTreeNode <T> *pBrother = direction == _left ? root->m_pRight : root->m_pLeft;
319 if (pBrother != NULL && pBrother->color == red)
320 {
321 pBrother->color = black;
322 root->color = red;
323 if (direction == _left)
324 {
325 rotate_left(root);
326 root = root->m_pLeft;
327 if (root != NULL)
328 {
329 pNode = root->m_pLeft;
330 pBrother = root->m_pRight;
331 }
332 }
333 else if (direction == _right)
334 {
335 rotate_right(root);
336 root = root->m_pRight;
337 if (root != NULL)
338 {
339 pNode = root->m_pRight;
340 pBrother = root->m_pLeft;
341 }
342 }
343 }
344 if (pBrother == NULL)
345 {
346 pNode = root;
347 if (root != NULL)
348 {
349 root = root->m_pParent;
350 if (root != NULL)
351 {
352 if (root->m_pLeft == pNode)
353 direction = _left;
354 else if (root->m_pRight == pNode)
355 direction = _right;
356 }
357 }
358 }
359 else if ((pBrother->m_pLeft == NULL || pBrother->m_pLeft->color == black) &&
360 (pBrother->m_pRight == NULL || pBrother->m_pRight->color == black))
361 {
362
363 pBrother->color = red;
364 pNode = root;
365 if (root != NULL)
366 {
367 root = root->m_pParent;
368 if (root != NULL)
369 {
370 if (root->m_pLeft == pNode)
371 direction = _left;
372 else if (root->m_pRight == pNode)
373 direction = _right;
374 }
375 }
376 }
377
378 else if (pBrother != NULL)
379 {
380 if (pBrother->m_pRight == NULL || pBrother->m_pRight->color == black)
381 {
382 if (pBrother->m_pLeft != NULL)
383 pBrother->m_pLeft->color = black;
384 pBrother->color = red;
385 if (direction == _left)
386 rotate_right(pBrother);
387 else if (direction == _right)
388 rotate_left(pBrother);
389 }
390 pBrother->color = root->color;
391 root->color = black;
392 if (direction == _left)
393 {
394 rotate_left(root);
395 if (pBrother->m_pRight != NULL)
396 pBrother->m_pRight->color = black;
397 }
398 else if (direction == _right)
399 {
400 rotate_right(root);
401 if (pBrother->m_pLeft != NULL)
402 pBrother->m_pLeft->color = black;
403 }
404 pNode = pRoot;
405 }
406 }
407 pNode->color = black;
408 }
409 template <typename T> void RedBlackTree <T>::rotate_left(RBTreeNode <T> *&root)
410 {
411 if (root == NULL || root->m_pRight == NULL)
412 return;
413 RBTreeNode <T> *pNode = root->m_pRight;
414 root->m_pRight = pNode->m_pLeft;
415 if (pNode->m_pLeft != NULL)
416 pNode->m_pLeft->m_pParent = root;
417 if (root->m_pParent == NULL)
418 pRoot = pNode;
419 else
420 {
421 if (root == root->m_pParent->m_pLeft)
422 root->m_pParent->m_pLeft = pNode;
423 else if (root == root->m_pParent->m_pRight)
424 root->m_pParent->m_pRight = pNode;
425 }
426 pNode->m_pParent = root->m_pParent;
427 pNode->m_pLeft = root;
428 root->m_pParent = pNode;
429 root = pNode;
430 }
431 template <typename T> void RedBlackTree <T>::rotate_right(RBTreeNode <T> *&root)
432 {
433 if (root == NULL || root->m_pLeft == NULL)
434 return;
435 RBTreeNode <T> *pNode = root->m_pLeft;
436 root->m_pLeft = pNode->m_pRight;
437 if (pNode->m_pRight != NULL)
438 pNode->m_pRight->m_pParent = root;
439 if (root->m_pParent == NULL)
440 pRoot = pNode;
441 else
442 {
443 if (root == root->m_pParent->m_pLeft)
444 root->m_pParent->m_pLeft = pNode;
445 else if (root == root->m_pParent->m_pRight)
446 root->m_pParent->m_pRight = pNode;
447 }
448 pNode->m_pParent = root->m_pParent;
449 pNode->m_pRight = root;
450 root->m_pParent = pNode;
451 root = pNode;
452 }
453 template <typename T> void RedBlackTree <T>::delete_all(RBTreeNode <T> *&root)
454 {
455 if (root == NULL)
456 return;
457 delete_all(root->m_pLeft);
458 delete_all(root->m_pRight);
459 delete root;
460 root = NULL;
461 }