平衡二叉查找树:红黑树

红黑树的定义


    红黑树是满足如下条件的二叉树:

(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 }

红黑树的删除操作

    这部分就麻烦多了。但是作为真男人,有困难要上,没困难,制造困难也要上......

    首先,红黑树是二叉树,它的删除跟普通二叉查找树如出一辙,问题还是在于维护它的结构。最理想的情况是,被删除的结点是一个红色结点,那么万事大吉,直接删掉就可以了,其他什么事都不用干。因为它不会影响上述(1)~(5)所有性质。

    如果删除的是黑色结点,那么就有如下几种情况了(这里也以被删除的结点都是其父亲的左孩子为例子,相反情况则镜像):

1.删除一个结点后,必然是它的一个孩子(可能为NULL)替补上来,如果这个孩子是红色的话,那就好办了,直接把这个替补结点染成黑色即可。

首先看看删除的结点只有一个孩子的情况:

这里d不管是被删除的结点a的左孩子还是右孩子,d都是替换a的,d最后成为了rt的左孩子。

如果被删除的结点有两个孩子的话:

这里要删除的结点是a,跟普通二叉查找树的删除一样,找到其后继f,f如果是后继,那么它必然无左子树。然后,把h替换到f这个位置,而把f替换到a这个位置。因为h是红色,直接染成黑色就可以了。

2.如果替补上来的结点也是黑色,这就是最麻烦的地方了。因为被删除的结点是黑色(假定为a结点),这必然导致它的某个祖先违反了性质(5),因为从该祖先到a的所有子树上的路径的黑色结点数都少了1,而替补上来的又恰好是黑的,没办法填补这个失去的一个黑色了。为了保持性质(5)不被破坏,《算法导论》里说把替补上来的结点当成有两重黑色,这样便填补了那个失去的一个黑色,但问题在于,这样的话替补结点违反了性质(1)!每个结点只能是黑色或红色,而这个替补结点却是双重黑色。于是,问题就在于如何把替补结点的双重黑色去掉一重,从而保持性质(1)不被破坏。

这里的解决方法按四步走,需要注意的是,删除后重新维护红黑树结构的四个步骤并不是一步到位的!而是通过重复走这四个步骤中的某几个,最终把性质(1)保持下来。

Step 1:替补结点的兄弟是红色的

这里要删除的结点是a,其后继为f,而替补f的h是黑色的。通过替换操作后,得到第二个图。h是双重黑色的,而它的兄弟g是红色的。这时的操作是,把g染成黑色,把e染成红色,得到第三个图。最后,将e进行一次左旋得到第四个图。需要注意的是,这时并不是说性质(1)就得到保持了,此时h的双重黑色还在,接下来的操作就得看结点
i 颜色了。如果结点 i 是红色,那么进行的还是Step 1,如果 i 是黑色,则进行其他步骤。

Step
2:替补结点的兄弟是黑色的,并且其兄弟结点的两个孩子都是黑色的

同样,经过替换后得到第二个图。此时h的兄弟g为黑色,并且g有两个黑色的孩子,这时把g染成红色,并把h那多余的一重黑色上升到e上面,如果e为红色,直接把e染成黑色就大功告成了。上述图示里e就恰好是红色。那如果e是黑色呢?此时e变成双重黑色了,接下来的操作,就看e的兄弟是什么状况了,然后再根据状况去选择步骤。

Step
3:替补结点的兄弟是黑色的,且其左孩子结点是红色的,右孩子的结点是黑色的。

照旧,先替换得到第二个图,这是h是双重黑色的,其兄弟g为黑色,且有一个红色左孩子 i
和黑色左孩子 j,这时把 i 染成黑色,并把兄弟g染成红色得到第三个图。对兄弟g进行右旋得到第四个图。此时,h的兄弟是 i 了,把 i
染成其父亲e的颜色,并把其父亲e和右孩子g染成黑色得到第五个图。最后对e进行一次左旋,得到第六个图。

Step
4:替补结点的兄弟是黑色的,且其右孩子结点是红色的。

替换得到的第二个图恰好跟 Step
3里的第四个图是一样的。其实这不是恰好,而是Step 3目的就是为了转换成Step 4的。到了Step
4,所以转换都完成了,因为h多余的那层黑色被转换到了其兄弟那颗树中去了(原本红色的 j 变成了黑色)。

    至此,删除的所有操作都结束了。需要注意的是,以上所有的“左”,“右”都是以“被删除结点是其父亲的左孩子”为例子而言的,如果不是,则恰好相反。

代码如下:

  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 }

View
Code

    完整的代码如下,由于没有采用《算法导论》里的“哨兵”方法,代码中多了很多判断语句,显得冗长。改日再改成“哨兵”的方法。

  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 }

View
Code

平衡二叉查找树:红黑树

时间: 2024-10-10 04:07:48

平衡二叉查找树:红黑树的相关文章

平衡搜索树--红黑树 RBTree

红黑树是一棵二叉搜索树,它在每个节点上增加了一个存储位来表示节点的颜色,可以是Red或Black. 通过对任何一条从根到叶子节点简单路径上的颜色来约束树的高度,红黑树保证最长路径不超过最短路径的两倍,因而近似于平衡. 红黑树是满足下面红黑性质的二叉搜索树: 1. 每个节点,不是红色就是黑色的 2. 根节点是黑色的 3. 如果一个节点是红色的,则它的两个子节点是黑色的(不存在连续的红色节点) 4. 对每个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点. 思考:为什么满足上面

查找算法总结(二分查找/二叉查找树/红黑树/散列表)

1.二分查找 二分查找时,先将被查找的键和子数组的中间键比较.如果被查找的键小于中间键,就在左子数组继续查找,如果大于中间键,就在右子数组中查找,否则中间键就是要找的元素. /** * 二分查找 */ public class BinarySearch { public static int find(int[] array, int key) { int left = 0; int right = array.length - 1; // while (left <= right)不能改为<

数据结构之红黑树

红黑树(Red Black Tree) 是一种自平衡二叉查找树 红黑树和AVL树类似,都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能.它虽然是复杂的,但它的最坏情况运行时间也是非常良好的,并且在实践中是高效的: 它可以在O(log n)时间内做查找,插入和删除,这里的n 是树中元素的数目.(度娘)C++ stl里面的set,map底层就是用红黑树实现的.红黑树具体的插入删除原理请参考<<算法导论>> 维基上面也讲得不错.反正插入过程就是要解决&q

浅谈AVL树,红黑树,B树,B+树原理及应用

背景:这几天在看<高性能Mysql>,在看到创建高性能的索引,书上说mysql的存储引擎InnoDB采用的索引类型是B+Tree,那么,大家有没有产生这样一个疑问,对于数据索引,为什么要使用B+Tree这种数据结构,和其它树相比,它能体现的优点在哪里? 看完这篇文章你就会了解到这些数据结构的原理以及它们各自的应用场景. 二叉查找树 简介 二叉查找树也称为有序二叉查找树,满足二叉查找树的一般性质,是指一棵空树具有如下性质: 任意节点左子树不为空,则左子树的值均小于根节点的值. 任意节点右子树不为

B树、B+树、红黑树、AVL树

定义及概念 B树 二叉树的深度较大,在查找时会造成I/O读写频繁,查询效率低下,所以引入了多叉树的结构,也就是B树.阶为M的B树具有以下性质: 1.根节点在不为叶子节点的情况下儿子数为 2 ~ M2.除根结点以外的非叶子结点的儿子数为 M/2(向上取整) ~ M3.拥有 K 个孩子的非叶子节点包含 k-1 个keys(关键字),且递增排列4.所有叶子结点在同一层,即深度相同 (叶节点可以看成是一种外部节点,不包含任何关键字信息) 在B-树中,每个结点中关键字从小到大排列,并且当该结点的孩子是非叶

【算法导论】红黑树详解之一(插入)

本文地址:http://blog.csdn.net/cyp331203/article/details/42677833 作者:苦_咖啡 欢迎转载,但转载请注明出处,否则将追究相应责任,谢谢!. 红黑树是建立在二叉查找树的基础之上的,关于二叉查找树可以参看[算法导论]二叉搜索树的插入和删除和[算法导论]二叉树的前中后序非递归遍历实现.对于高度为h的二叉查找树而言,它的SEARCH.INSERT.DELETE.MINIMUM.MAXIMUM等操作的时间复杂度均为O(h).所以在二叉查找树的高度较高

AVL树、红黑树以及B树介绍

简介 首先,说一下在数据结构中为什么要引入树这种结构,在我们上篇文章中介绍的数组与链表中,可以发现,数组适合查询这种静态操作(O(1)),不合适删除与插入这种动态操作(O(n)),而链表则是适合删除与插入,而查询效率则就比较慢了,本文要分享学习的树就是为了平衡这种静态操作与动态操作的差距. 一.二叉查找树 简介 满足下面条件就是二叉查找树 任意节点左子树不为空,则左子树的值均小于根节点的值. 任意节点右子树不为空,则右子树的值均大于于根节点的值. 任意节点的左右子树也分别是二叉查找树. 没有键值

红黑树基本特点,及其建立——转

红黑树定义: 红黑树是一种自平衡二叉查找树,红黑树和AVL都是BST(二叉排序树)的平衡版本,相比于AVL的完全平衡,红黑树只要求局部平衡,因此当向红黑树插入和删除结点时,需要调整的比AVL要少,统计性能要高于AVL树,C++ STL中的map.set.multimap和multiset都应用了红黑树的变体.AVL插入结点调整见:平衡二叉树-AVL树(LL.RR.LR.RL旋转). 红黑树的特点: (1)每个节点或者是黑色,或者是红色. (2)根节点是黑色. (3)每个叶子节点(NIL)是黑色.

笔试算法题(51):简介 - 红黑树(RedBlack Tree)

红黑树(Red-Black Tree) 红黑树是一种BST,但是每个节点上增加一个存储位表示该节点的颜色(R或者B):通过对任何一条从root到leaf的路径上节点着色方式的显示,红黑树确保所有路径的差值不会超过一倍,最终使得BST接近平衡: 红黑树内每个节点包含五个属性:color, key, left, right和p,p表示指向父亲节点的指针:一棵BST需要同时满足下述五个性质才能称作红黑树: 每个节点只能是红色或者黑色节点中的一种: 根节点必须是黑色: 每个叶节点(NULL)必须是黑色:

红黑树、B(+)树、跳表、AVL等数据结构,应用场景及分析,以及一些英文缩写

在网上学习了一些材料. 这一篇:https://www.zhihu.com/question/30527705 AVL树:最早的平衡二叉树之一.应用相对其他数据结构比较少.windows对进程地址空间的管理用到了AVL树 红黑树:平衡二叉树,广泛用在C++的STL中.map和set都是用红黑树实现的.我们熟悉的STL的map容器底层是RBtree,当然指的不是unordered_map,后者是hash. B/B+树用在磁盘文件组织 数据索引和数据库索引 Trie树 字典树,用在统计和排序大量字符