算法导论第十三章 红黑树

  写在前面:这一章真的把我害惨了,之前至少尝试看过3遍,每次看之前都下定决定一定要把它拿下,可是由于内容较多,深度够深,以致于每次要不是中途有什么事放弃了就跳过了,要不是花时间太多仍然不能理解而放弃。这次总算挺过来了,前后零零散散的时间加起来差不多也有两天时间。这次能坚持下来并攻克,我想大概有这么几个原因吧:第一是之前下定的决心要写一个最新版《算法导论》的读书笔记,之前几章都坚持写了,不能让这个成为拦路虎,即使再难再花时间都要弄懂;第二是通过前面几章的动手实践,发现自己的理解能力、动手能力都进步了,自然这章理解起来也不那么费力了;第三,如果有,那就是现在懂的东西多了,视野开阔了^-^。但说实话,也是费了不少心血,看了一下自己的打的草稿,超过十页以上,密密麻麻都是一些红黑树,这些努力我觉得都是值得的,但我之所以说“把我害惨了”,甚至有点不甘的是:我好大一部分时间都花在了调试代码上,原因是粗心大意写错了一些变量、指针......这一章由于涉及到多个指针的替换,所以切记在写的时候一定足够专注,尽量一口气写完,不要拖。

一、红黑树概览

  红黑树是一种平衡二叉树,什么是平衡二叉树?我的理解是加上”平衡条件“的二叉搜索树。其实这样的理解还不准确,因为二叉搜索树只在某些特殊的情况下是不平衡的。比如下图所示:

  所以,所谓树形平衡与否,并没有一个绝对的标准,”平衡“ 的大致意义是:没有任何一个结点深度过大。二叉搜索树在某些特殊情况下,无法维持绝对的平衡,所以,其动态集合操作,最坏的时间复杂度为O(n)。因此就出现一些通过加上某种”平衡条件“来促使二叉搜索树达到绝对的平衡(确保整棵树的深度维持在O(lgn))。红黑树的”平衡条件“是:赋予结点不同颜色,并对根结点到任何叶子结点的颜色进行约束。这样的平衡不算太好,近似平衡,但性能已经比二叉搜索树提升了不少。

  红黑树不仅是二叉搜索树,且必须满足以下5条平衡规则:

1)每个结点或是红色,或是是黑色。
2)根结点是黑的。
3)所有的叶结点(NULL)是黑色的。(NULL被视为一个哨兵结点,所有应该指向NULL的指针,都看成指向了NULL结点。)
4)如果一个结点是红色的,则它的两个儿子节点都是黑色的。
5)对每个结点,从该结点到其子孙结点的所有路径上包含相同数目的黑结点。

简单的记法就是:红黑 黑 黑 红黑黑 黑

黑高度的定义: 从某个结点出发(不包括该结点)到达一个叶结点的任意一条路径上,黑色结点的个数成为该结点x的黑高度。红黑树的黑高度定义为其根结点的黑高度。

二、平衡二叉树历史概览

  最好的平衡是形如满二叉树这种,所以可以把全是黑色节点的满二叉树看做是红黑树的一个特列,其性能是最好的。但是是无论如何也不可能找到这样的平衡条件,有一种树退而求其次,它的平衡条件是要求任何结点的左右子树高度相差不超过1,就是AVL树。AVL树是最早提出的将搜索树平衡化的想法的实践。此外,还有由J.E.Hopcroft提出的一种”2-3“树,这种树是通过操纵结点的度数来维持平衡的。Bayer提出一种”2-3“树的推广,B树。Anderson提出了一种代码更简单的红黑树变种,称为AA树,AA树和红黑树类似,只是左边孩子永远不能为红色。还有一种treap树则是由Seidel和Aragon提出的。

  此外,平衡二叉树还有很多变种,包括带权的平衡树、k近邻树,以及替罪羊树,还有一种比较有趣的”伸展树“,伸展树不需要明确的平衡条件来维持平衡,替代的是,每次存取时的”伸展操作“在树内进行,后面会涉及到。另外还有就是跳表,跳表是扩展了一些额外指针的链表。

  但是,红黑树是真正的在实际中得到大量应用的复杂数据结构:C++STL中的关联容器map,set都是红黑树的应用(所以标准库容器的效率太好了,能用标准库容器尽量使用标准库容器);Linux内核中的用户态地址空间管理也使用了红黑树。

三、红黑树实现

经验之谈:

1)插入删除和二叉搜索树类似,插入的结点必须着红色(因为如果是黑色,是一定会破坏性质5,难以修复,而如果是红色,则可能破坏性质2和4,容易修复);

2)插入修复三种情况:发生在插入结点的父结点为红色的情况下,即破坏了性质4,这个时候考虑插入点的uncle结点进行修复;性质2破坏,直接着黑色;

3)删除恢复四种情况:发生在删除结点为黑色的情况下,即破坏了性质5,这个时候考虑删除点的brother结点进行修复;

4)旋转操作注意指针的指向,每个都要考虑全了,parent、left、right缺一不可。

如果按照《算法导论》书的步骤一步步往下看,是一定看得懂的,因为书上的东西是写的最全的,网友写的博客虽然有些也不错,但都是经过自己过滤过的,且不说语言表达怎样,肯定没有书本记录得详细。只是有些地方书本上表达得太深奥,可以借助一些博客来理解。比如说我在看到删除修复的四种情况时,书上说的什么”双重黑色、红+黑,x既不是黑色,也不是红色“,把我搞得稀里糊涂的,看了之后整个人都不好了,后来看了July的博客才弄懂了个大概(见后面的参考引文),再回过头来看就发现原来如此。

关于红黑树查找、删除等具体的细节就不再做过多的赘述,这里只记录下自己学习了之后的一些规律总结及心得。

关于旋转:

旋转有些书分为单旋和双旋,双旋顾名思义就是单旋两次,单旋又分为左旋和右旋,操作是对称的。旋转操作对于理解树的指针指向是再好不过了,就像理解链表的指针指向再好不过是元素的插入了。这里要确保一个结点的三个指针:parent、left、right都要更新了。书上没说具体的方法论,如果让我们在纸上写个左旋,估计好多人都要跪,因为指针指来指去,没有思路完全不行。根据我的经验,总结这样的一个规律(仅供参考):

就拿左旋作为例子,如下图所示:

规律可以总结成3个字:补——>提——>降

注意:图2由于纸张不够的原因,代码没写全,见下面的代码部分:

附上左旋的代码(C++模板类):

 1 //左旋
 2 template<typename TKey, typename TValue>
 3 void RBTree<TKey, TValue>::_LeftRotate( RBTreeNode *x_node )
 4 {
 5     //assert
 6     if ( !(x_node->isValid() && x_node->Right->isValid()) )
 7         throw exception( "左旋操作要求对非哨兵进行操作,并且要求右孩子也不是哨兵" );
 8
 9     RBTreeNode *y_node = x_node->Right;
10
11     //以下三步的宗旨是用 y 替换 x,注意将 x 的Parent、Left、Right都换成 y 的
12     // 1) x 和 y 分离 (补)
13     x_node->Right = y_node->Left;
14     if (y_node->Left != m_pNil)
15         y_node->Left->Parent = x_node;
16
17     // 2) 设置y->Parent (提)
18     y_node->Parent = x_node->Parent;
19     if (x_node->Parent == m_pNil)
20         m_pRoot = y_node;
21     else if (x_node->Parent->Left == x_node)
22         x_node->Parent->Left = y_node;
23     else
24         x_node->Parent->Right = y_node;
25
26     // 3) 设置y->Left (降)
27     y_node->Left = x_node;
28     x_node->Parent = y_node;
29 }

关于删除修复的”双重黑、红+黑“:

  如何理解?这个地方,书上没说明白,在说这个意思之前,我们先来看看红黑树的删除修复究竟是怎么个回事?

  红黑树的删除务必不能破坏了红黑树的5条性质,但这是不可能的,如果删除的结点破坏了5条中任何一条性质,这个时候就需要采用措施进行修复,我们分析一下:删除什么结点会破坏性质,破坏哪条性质?

1)如果删除的是红色结点,则无影响;

2)如果删除的是黑色结点,则不用想,第5条性质破坏了,其中:

  a)如果这个黑色结点是根结点,同时根结点的非空子结点,即将要替换它的结点为红色,则破坏性质2;

  b)如果这个黑色结点的父结点和非空子结点都为红色,则破坏性质4。

知道了这点,我们再来看下什么是”双重黑、红+黑“,其实,这个说法主要是一种假设,假设存在着这样的节点,那么红黑树的性质就满足了,但实际上这样的结点是不存在的,所以需要转换,而转换的过程就是修复的过程。说白了,这个假设是为了便于代码实现,为了方便完成四种修复操作的一个假设性规律。因为删除修复不像插入修复那么明显,有了它就像找到什么诀窍一样,删除的四种修复不用”强制性记忆“就能明白为什么要这样做^-^。

红黑树的删除与二叉搜索树的删除基本一样,不同之处在于需要记录替换被删结点到那个结点,然后以它为根进行修复。”双重黑、红+黑“就体现在这里,如下两图所示:

  其中,delete结点被其后继结点 x (这里两种情况:一是后继就是delete的右孩子,二是比delete大的最小结点)替换。需要修复的条件是:删除结点得是黑色,如果 x 也是黑色,则称为”双黑“;如果 x 是红色,则称为”红黑“。好了,知道了这点,在对照着删除修复的四种情况看,就很容易懂了,其修复的过程就是看 x 的颜色情况和 x的兄弟结点的颜色情况,有双重黑的,就去掉一重黑,使之平衡,四种情况分别有不同的去重情况,整个过程是很好理解的,具体的细节就不做赘述,想必知道这点,整个删除修复就很好理解了。

  这一章我觉得难点就在于删除修复,插入修复是比较容易想到的,然后我认为需要着重注意的地方都记录下来了,下面贴上自己写的基于C++模板的代码,有点长。

1)red_black_tree.h

  1 #ifndef __RED_BLACK_TREE_
  2 #define __RED_BLACK_TREE_
  3
  4 //使用模板类
  5 template< typename TKey, typename TValue >
  6 class RBTree
  7 {
  8 /************************************************************************/
  9 /* 红黑树结点属性
 10 /************************************************************************/
 11 public:
 12     //结点的颜色-枚举
 13     enum RBTreeNodeColor {
 14         RED,    //红色
 15         BLACK    //黑色
 16     };
 17
 18     //结点的属性
 19     struct RBTreeNode {
 20         TKey                Key;
 21         TValue                Value;
 22         RBTreeNodeColor        Color;
 23         RBTreeNode            *Parent;
 24         RBTreeNode            *Left;
 25         RBTreeNode            *Right;
 26
 27         //@brief 红黑树中的哨兵结点T.nil实际上是空结点,为无效结点
 28         //@return 返回某个结点是否为无效结点
 29         //@retval = true 非nil结点
 30         //@retval = false nil结点
 31         inline bool isValid() const {
 32             return ( this != m_pNil );
 33         }
 34     };
 35
 36 /************************************************************************/
 37 /* 红黑树公有属性
 38 /************************************************************************/
 39 public:
 40     RBTree();    //构造函数
 41     ~RBTree();    //析构函数
 42
 43     //@brief 插入一个结点
 44     bool Insert( TKey key, TValue value );
 45
 46     //@brief 删除一个结点
 47     bool Delete( TKey key );
 48
 49     //@brief 搜索一个结点
 50     RBTreeNode * Search( TValue const &value );
 51
 52     //@brief 判断红黑树是否为空
 53     bool Empty();
 54
 55     //@brief 显示当前红黑树
 56     void Display() const;
 57
 58 /************************************************************************/
 59 /* 红黑树私有属性
 60 /************************************************************************/
 61 private:
 62     //@brief 递归删除所有结点
 63     void _RecursiveReleaseNode ( RBTreeNode *node );
 64
 65     //@brief 显示
 66     void _Display( RBTreeNode *node ) const;
 67
 68     //@brief 真正的插入函数
 69     void _InsertRBTree( RBTreeNode *node );
 70
 71     //@brief 对插入操作的修复
 72     void _InsertFixup( RBTreeNode *node );
 73
 74     //@brief 左旋
 75     void _LeftRotate( RBTreeNode *node );
 76
 77     //@brief 右旋
 78     void _RightRotate( RBTreeNode *node );
 79
 80     //@brief 真正的删除操作
 81     void _Delete( RBTreeNode *node );
 82
 83     //@brief 专属红黑树的替换操作
 84     void _RB_Transplant( RBTreeNode *unode, RBTreeNode *vnode );
 85
 86     //@brief 对删除操作的修复
 87     void _DeleteFixup( RBTreeNode *node );
 88
 89     //@brief 后继
 90     RBTreeNode * _Successor( RBTreeNode *node );
 91
 92     //@brief 前驱
 93     RBTreeNode * _Predecessor( RBTreeNode *node );
 94
 95     //@brief Maximum
 96     RBTreeNode * _Maximum( RBTreeNode *node );
 97
 98     //@brief Minimum
 99     RBTreeNode * _Minimum( RBTreeNode *node );
100
101
102 private:
103     //红黑树的数据成员
104     RBTreeNode    *m_pRoot;    //根结点
105     static RBTreeNode    *m_pNil;    //哨兵空结点
106 };
107 #endif//__RED_BLACK_TREE_

2)red_black_tree.cpp

  1 #include <iostream>
  2 #include <ctime>
  3 #include <cassert>
  4 #include <sstream>
  5 using namespace std;
  6
  7 #include "red_black_tree.h"
  8
  9 //静态成员变量初始化
 10 template<typename TKey, typename TValue>
 11 typename RBTree<TKey, TValue>::RBTreeNode * RBTree<TKey, TValue>::m_pNil = NULL;
 12
 13 template<typename TKey, typename TValue>
 14 RBTree<TKey, TValue>::RBTree()
 15 {
 16     if ( !m_pNil ) {
 17         //叶结点是一个特殊的黑结点
 18         m_pNil = new RBTreeNode();
 19         m_pNil->Color = BLACK;
 20     }
 21     m_pRoot = m_pNil;
 22 }
 23
 24 template<typename TKey, typename TValue>
 25 RBTree<TKey, TValue>::~RBTree()
 26 {
 27     _RecursiveReleaseNode( m_pRoot );
 28 }
 29
 30 template<typename TKey, typename TValue>
 31 void RBTree<TKey, TValue>::_RecursiveReleaseNode( RBTreeNode *node )
 32 {
 33     if ( node->isValid() ) {
 34         _RecursiveReleaseNode( node->Left );
 35         _RecursiveReleaseNode( node->Right );
 36         delete node;
 37     }
 38 }
 39
 40 template<typename TKey, typename TValue>
 41 bool RBTree<TKey, TValue>::Empty()
 42 {
 43     return !(m_pRoot->isValid());
 44 }
 45
 46 //左旋
 47 template<typename TKey, typename TValue>
 48 void RBTree<TKey, TValue>::_LeftRotate( RBTreeNode *x_node )
 49 {
 50     //assert
 51     if ( !(x_node->isValid() && x_node->Right->isValid()) )
 52         throw exception( "左旋操作要求对非哨兵进行操作,并且要求右孩子也不是哨兵" );
 53
 54     RBTreeNode *y_node = x_node->Right;
 55
 56     //以下三步的宗旨是用 y 替换 x,注意将 x 的Parent、Left、Right都换成 y 的
 57     // 1) x 和 y 分离 (补)
 58     x_node->Right = y_node->Left;
 59     if (y_node->Left != m_pNil)
 60         y_node->Left->Parent = x_node;
 61
 62     // 2) 设置y->Parent (提)
 63     y_node->Parent = x_node->Parent;
 64     if (x_node->Parent == m_pNil)
 65         m_pRoot = y_node;
 66     else if (x_node->Parent->Left == x_node)
 67         x_node->Parent->Left = y_node;
 68     else
 69         x_node->Parent->Right = y_node;
 70
 71     // 3) 设置y->Left (降)
 72     y_node->Left = x_node;
 73     x_node->Parent = y_node;
 74 }
 75
 76 //右旋
 77 template<typename TKey, typename TValue>
 78 void RBTree<TKey, TValue>::_RightRotate( RBTreeNode *x_node )
 79 {
 80     //assert
 81     if ( !(x_node->isValid() && x_node->Left->isValid()) )
 82         throw exception( "右旋操作要求对非哨兵进行操作,并且要求左孩子也不是哨兵" );
 83     RBTreeNode *y_node = x_node->Left;
 84
 85     //以下三步的宗旨是用 y 替换 x,注意将 x 的Parent、Left、Right都换成 y 的
 86     // 1) x 和 y 分离 (补)
 87     x_node->Left = y_node->Right;
 88     if (y_node->Right != m_pNil)
 89         y_node->Right->Parent = x_node;
 90
 91     // 2) 设置y->Parent (提)
 92     y_node->Parent = x_node->Parent;
 93     if (x_node->Parent == m_pNil)
 94         m_pRoot = y_node;
 95     else if (x_node->Parent->Right == x_node)
 96         x_node->Parent->Right = y_node;
 97     else
 98         x_node->Parent->Left = y_node;
 99
100     // 3) 设置y->Left (降)
101     y_node->Right = x_node;
102     x_node->Parent = y_node;
103 }
104
105 //搜索
106 template<typename TKey, typename TValue>
107 typename RBTree<TKey, TValue>::RBTreeNode* RBTree<TKey, TValue>::Search( TValue const &value )
108 {
109     RBTreeNode *node = m_pRoot;
110     while( node != m_pNil && node->Value != value )
111         node = ((value < node->Value) ? node->Left:node->Right);
112     return node;
113 }
114
115 //插入
116 template<typename TKey, typename TValue>
117 bool RBTree<TKey, TValue>::Insert( TKey key, TValue value )
118 {
119     if ( Search(value)->isValid() )
120         //value 重复,添加失败
121         return false;
122     else {
123         //新添加的节点为红结点,left=right=nil
124         RBTreeNode *new_node = new RBTreeNode();
125         new_node->Key = key;
126         new_node->Value = value;
127         new_node->Color = RED;
128         new_node->Left = new_node->Right = m_pNil;
129
130         //插入
131         _InsertRBTree(new_node);
132         //修复
133         _InsertFixup(new_node);
134         return true;
135     }
136 }
137
138 //真正的插入:与二叉搜索树几乎一样
139 template<typename TKey, typename TValue>
140 void RBTree<TKey, TValue>::_InsertRBTree( RBTreeNode *new_node )
141 {
142     RBTreeNode *current_node = m_pNil;
143     RBTreeNode *next_node = m_pRoot;
144
145     //找到插入点
146     while ( next_node != m_pNil ) {
147         current_node = next_node;
148         next_node = ( new_node->Value < next_node->Value )? next_node->Left: next_node->Right;
149     }
150
151     new_node->Parent = current_node;
152     if ( current_node == m_pNil ) //空树
153         m_pRoot = new_node;
154     else if ( new_node->Value < current_node->Value )
155         current_node->Left = new_node; //插入左子树
156     else
157         current_node->Right = new_node; //插入右子树
158
159     //设置new_node-left-right = nil, color = red
160     new_node->Left = m_pNil;
161     new_node->Right = m_pNil;
162     new_node->Color = RED;    //插入结点为红色
163 }
164
165 //插入修复
166 //new_node -> z, uncle_node -> y;
167 template<typename TKey, typename TValue>
168 void RBTree<TKey, TValue>::_InsertFixup( RBTreeNode *new_node )
169 {
170 //////////////////////////////////////////////////////////////////////////
171 //啰嗦的写法
172 /*
173     while ( new_node->Parent->Color == RED ) {
174         RBTreeNode *uncle_node = new RBTreeNode();
175         if ( new_node->Parent == new_node->Parent->Parent->Left ) { //new的父结点为祖父结点的左孩子
176             uncle_node = new_node->Parent->Right; //uncle结点在右边
177
178             if ( uncle_node->Color == RED ) { //case1: new 的叔结点为红色
179                 new_node->Color = BLACK;
180                 uncle_node->Color = BLACK;
181                 new_node->Parent->Color = RED;
182                 new_node = new_node->Parent->Parent;
183             }
184
185             else if ( uncle_node->Color == BLACK && new_node->Parent->Right )  //case2: new 叔结点为黑色且new是一个右孩子
186                 _LeftRotate( new_node->Parent );
187
188             new_node->Parent->Color = BLACK;    //case3: new 叔结点为黑色且new是左孩子
189             new_node->Parent->Parent->Color = RED;
190             _RightRotate( new_node->Parent->Parent );
191         }
192         else { //和上面对称的相同操作
193             ///
194         }
195     }
196 */
197 //////////////////////////////////////////////////////////////////////////
198 //精炼的写法
199     while ( new_node->Parent->Color == RED ) {
200
201         //标识new的父结点是否是祖父结点的左孩子
202         bool parent_is_left_child_flag = ( new_node->Parent == new_node->Parent->Parent->Left );
203         RBTreeNode *uncle_node = ( parent_is_left_child_flag ? new_node->Parent->Parent->Right: new_node->Parent->Parent->Left );
204
205         if ( uncle_node->Color == RED ) { //case1: new 的叔结点为红色
206             new_node->Parent->Color = BLACK;  //!!!
207             uncle_node->Color = BLACK;
208             new_node->Parent->Parent->Color = RED; //!!!这两个地方写错了,tmd,还得老子改了大半天
209             new_node = new_node->Parent->Parent;
210         }
211
212         else {
213             if ( new_node == ( parent_is_left_child_flag ? new_node->Parent->Right: new_node->Parent->Left ) ) {//case2: new 叔结点为黑色且new是一个右孩子
214                 new_node = new_node->Parent;
215                 parent_is_left_child_flag ? _LeftRotate( new_node ):_RightRotate( new_node );
216             }
217
218             new_node->Parent->Color = BLACK;    //case3: new 叔结点为黑色且new是左孩子
219             new_node->Parent->Parent->Color = RED;
220             parent_is_left_child_flag ? _RightRotate( new_node->Parent->Parent ): _LeftRotate( new_node->Parent->Parent );
221         }
222     }
223     m_pRoot->Color = BLACK;
224 }
225
226 //删除
227 template<typename TKey, typename TValue>
228 bool RBTree<TKey, TValue>::Delete( TKey key )
229 {
230     //没有找到该结点
231     RBTreeNode *delete_node = Search( key );
232     if ( !(delete_node->isValid()) ) //!isValid()
233         return false;
234     else {
235         _Delete( delete_node );
236         return true;
237     }
238 }
239
240 //真正的删除
241 //形如二叉搜索树的删除,只不过需要记录待删除节点的右孩子的颜色值
242 //delete_node -> z; min_node -> y; temp_node -> x
243 template<typename TKey, typename TValue>
244 void RBTree<TKey, TValue>::_Delete( RBTreeNode *delete_node )
245 {
246     RBTreeNode *temp_node = delete_node;
247     int color = delete_node->Color; //记录待删除结点原来的颜色
248
249     if ( delete_node->Left == m_pNil ) { //左孩子为空
250         temp_node = delete_node->Right;
251         _RB_Transplant( delete_node, delete_node->Right );
252     }
253     else if ( delete_node->Right == m_pNil ) {//右孩子为空
254         temp_node = delete_node->Left;
255         _RB_Transplant( delete_node, delete_node->Left );
256     }
257     else {
258         RBTreeNode *min_node = _Minimum( delete_node->Right ); //delete的后继结点min_node
259         temp_node = min_node->Right;
260         color = min_node->Color; //更新color
261
262         if ( min_node->Parent == delete_node )
263             temp_node->Parent = min_node;
264
265         else {
266             _RB_Transplant( min_node, min_node->Right );
267             min_node->Right = delete_node->Right;
268             min_node->Right->Parent = min_node;
269         }
270         _RB_Transplant( delete_node, min_node );
271         min_node->Left = delete_node->Left;
272         min_node->Left->Parent = min_node;
273         min_node->Color = delete_node->Color;
274     }
275     if ( color == BLACK )
276         _DeleteFixup( temp_node ); //template_node x
277 }
278
279 //删除修复
280 template<typename TKey, typename TValue>
281 void RBTree<TKey, TValue>::_DeleteFixup( RBTreeNode *node )
282 {
283     while ( node != m_pRoot && node->Color == BLACK ) {
284         //标识node是否是其父结点的左孩子
285         bool node_is_left_child_flag = ( node == node->Parent->Left );
286         //node的兄弟节点
287         RBTreeNode *brother = ( node_is_left_child_flag ? node->Parent->Right : node->Parent->Left );
288
289         //case1 node的兄弟结点为红色,一次旋转操作变成case2
290         if ( brother->Color == RED ) {
291             node->Parent->Color = RED;
292             brother->Color = BLACK;
293             node_is_left_child_flag ? _LeftRotate( node->Parent ):_RightRotate( node->Parent );
294
295             //更新brother结点
296             brother = ( node_is_left_child_flag ? node->Parent->Right : node->Parent->Left );
297         }
298
299         //case2 node 的兄弟结点为黑色,且brother两个孩子结点皆为黑色
300         if ( brother->Left->Color == BLACK && brother->Right->Color == BLACK ) {
301             brother->Color = RED;
302             node = node->Parent; //node更新为上一层
303         }
304
305         //case3 node的兄弟结点为黑色,且brother的左孩子为红色,右孩子为黑色
306         else {
307             if ( ( node_is_left_child_flag ? brother->Right->Color : brother->Left->Color ) == BLACK ) {
308                 brother->Color = RED;
309                 //注意左右的不同
310                 ( node_is_left_child_flag ? brother->Left->Color : brother->Right->Color ) = BLACK;
311                 node_is_left_child_flag ? _RightRotate( brother ):_LeftRotate( brother );
312                 brother = ( node_is_left_child_flag ? node->Parent->Right:node->Parent->Left ); //更新brother结点 -> 变成case4
313             }
314
315             //case4 node结点的兄弟结点为黑色,且brother 结点的右孩子为红色
316             brother->Color = /*node->Parent->Color*/RED;
317             node->Parent->Color = BLACK;
318
319             ( node_is_left_child_flag ? brother->Right->Color : brother->Left->Color ) = BLACK;
320             node_is_left_child_flag ? _LeftRotate( node->Parent ):_RightRotate( node->Parent );
321
322             node = m_pRoot; //最后赋给root
323         }
324     }
325     //最后只需要简单置x为黑结点就可以,_root的改变已经由左右旋自动处理了
326     node->Color = BLACK;
327 }
328
329 //RB替换操作
330 template<typename TKey, typename TValue>
331 void RBTree<TKey, TValue>::_RB_Transplant( RBTreeNode *unode, RBTreeNode *vnode )
332 {
333     if ( unode->Parent == m_pNil )
334         m_pRoot = vnode;
335     else if ( unode->Parent->Left == unode )
336         unode->Parent->Left = vnode;
337     else
338         unode->Parent->Right = vnode;
339     vnode->Parent = unode->Parent; //!!!别漏了
340 }
341
342 //显示
343 template<typename TKey, typename TValue>
344 void RBTree<TKey, TValue>::Display() const
345 {
346     _Display( m_pRoot );
347     std::cout << std::endl;
348 }
349
350 template<typename TKey, typename TValue>
351 void RBTree<TKey, TValue>::_Display( RBTreeNode *node ) const
352 {
353 //     if ( node->isValid() ) {
354 //         cout << node->Value << " ";
355 //         if (node->Left->isValid())
356 //             _Display(node->Left);
357 //         if (node->Right->isValid())
358 //             _Display(node->Right);
359 //     }
360     if ( node->Parent == m_pNil )
361         cout << "root: " << node->Value << "( " << node->Color << " )" << endl;
362     else if ( node->Parent->Left == node )
363         cout << "left: " << node->Value << "( " << node->Color << " )" << " " << "parent: " << node->Parent->Value << "( " << node->Parent->Color << " )" << endl;
364     else cout << "right: " << node->Value << "( " << node->Color << " )" << " " << "parent: " << node->Parent->Value << "( " << node->Parent->Color << " )" << endl;
365     if ( node->Left->isValid() )
366         _Display(node->Left);
367     if ( node->Right->isValid() )
368         _Display(node->Right);
369 }
370
371 //@brief 后继
372 template<typename TKey, typename TValue>
373 typename RBTree<TKey, TValue>::RBTreeNode * RBTree<TKey, TValue>::_Successor( RBTreeNode *node )
374 {
375     if(node->m_pRight)
376         return _GetMaximum(node);
377
378     _Node *pTemp = node->m_pParent;
379     while (pTemp && node == pTemp->m_pRight ) {
380         node = pTemp;
381         pTemp = pTemp->m_pParent;
382     }
383     return pTemp;
384 }
385
386 //@brief 前驱
387 template<typename TKey, typename TValue>
388 typename RBTree<TKey, TValue>::RBTreeNode * RBTree<TKey, TValue>::_Predecessor( RBTreeNode *node )
389 {
390     if (node->m_pLeft)
391         return _GetMinimum(node);
392
393     _Node *pTemp = node->m_pParent;
394     while (pTemp && node == pTemp->m_pLeft) {
395         node = pTemp;
396         pTemp = pTemp->m_pParent;
397     }
398     return pTemp;
399 }
400
401 //@brief Maximum
402 template<typename TKey, typename TValue>
403 typename RBTree<TKey, TValue>::RBTreeNode * RBTree<TKey, TValue>::_Maximum( RBTreeNode *node )
404 {
405     while( node->Right != m_pNil ) {
406         node = node->Right;
407     }
408     return node;
409 }
410
411 //@brief Minimum
412 template<typename TKey, typename TValue>
413 typename RBTree<TKey, TValue>::RBTreeNode * RBTree<TKey, TValue>::_Minimum( RBTreeNode *node )
414 {
415     while( node->Left != m_pNil ) {
416         node = node->Left;
417     }
418     return node;
419 }
420
421 int main()
422 {
423     srand((unsigned)time(NULL));
424     RBTree<int, int> rbt;
425     int ninsert[] = {12, 1, 9, 2, 0, 11, 7, 19, 4, 15, 18, 5, 14, 13, 10, 16, 6, 3, 8, 17};
426     //int ninsert[] = {3,7,12,10,14,15,16,17,21,19,20,23,26,41,30,28,38,35,39,47};
427     int n = sizeof(ninsert)/sizeof(ninsert[0]);
428
429     //用随机值生成一棵二叉查找树
430     for ( int i = 0; i < n; ++i )
431     {
432         //int v = rand() % 100;
433         rbt.Insert( ninsert[i], ninsert[i] );
434         //rbt.Insert( i, i );
435     }
436     rbt.Display();
437
438 //     for ( int i = 0; i < n; i ++)
439 //         rbt.Delete( ninsert[i] );
440
441 //     删除所有的小奇数
442     for ( int i = 1; i < n; ++i )
443     {
444         if ( i % 2 == 1 /*&& i < 50*/ )
445         {
446             if ( rbt.Delete( i ) )
447             {
448                 cout << "Deleted [" << i << "]" << endl;
449             }
450         }
451     }
452     rbt.Display();
453 //         //删除所有的大偶数
454 //         for ( int i = 0; i < 100; ++i )
455 //         {
456 //             if ( i % 2 == 0 && i > 50 )
457 //             {
458 //                 if ( rbt.Delete( i ) )
459 //                 {
460 //                     cout << "Deleted [" << i << "]" << endl;
461 //                 }
462 //             }
463 //         }
464 //         rbt.Display();
465
466     return 0;
467 }

时间: 2024-10-18 11:26:52

算法导论第十三章 红黑树的相关文章

算法导论 第十三章 红黑树(python)-1插入

红黑树是上一章二叉搜索树的改进,实现一种平衡 ,保证不会出现二叉树变链表的情况,基本动态集合操作的时间复杂度为O(lgn) 实际用途:c++stl中的set,map是用他实现的 红黑树的性质: 1.每个结点或是红色的,或是黑色的 2.跟结点是黑色的 3.每个叶结点(NIL)是黑色 4.如果一个结点是红色的,则它的两个结点都是黑色的 5.对每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同的数目的黑色结点(数目被称为黑高bh(x)) 如下图: (T.nil 哨兵后面被忽略 None) 红

算法导论 第13章 红黑树

二叉查找树的基本操作包括搜索.插入.删除.取最大和最小值等都能够在O(h)时间复杂度内实现,因此能在期望时间O(lgn)下实现,但是二叉查找树的平衡性在这些操作中并没有得到维护,因此其高度可能会变得很高,当其高度较高时,而二叉查找树的性能就未必比链表好了,所以二叉查找树的集合操作是期望时间O(lgn),最坏情况下为O(n). 红黑树也是一种二叉查找树,它拥有二叉查找树的性质,同时红黑树还有其它一些特殊性质,这使得红黑树的动态集合基本操作在最坏情况下也为O(lgn),红黑树通过给节点增加颜色和其它

算法导论 第十三章:红黑树

红黑树(red-black tree)是一种"平衡"查找树,它能保证最坏情况下,基本的动态集操作时间为O(lgn). 性质: 1)每个节点要么是红的,要么是黑的 2)根节点和叶子节点(NIL)是黑色的 3)若一个节点是红色的,则他的两个孩子节点是黑色的 4)对于每一个节点x,从该节点到其子酸节点的所有路径上包含相同数目的黑节点(#black nodes = black-height(x)) 引理: 一棵有n个内节点的红黑树的高度至多为 2 lg(n+1) 红黑树上插入删除的完整代码如下

第十三章 红黑树

R-B Tree简介 R-B Tree,全称是Red-Black Tree,又称为“红黑树”,它一种特殊的二叉查找树.红黑树的每个节点上都有存储位表示节点的颜色,可以是红(Red)或黑(Black). 红黑树的特性:(1)每个节点或者是黑色,或者是红色.(2)根节点是黑色.(3)每个叶子节点(NIL)是黑色. [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!](4)如果一个节点是红色的,则它的子节点必须是黑色的.(5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点.

完整的C++实现算法导论十三章红黑树以及十四章中的顺序统计树

#include<iostream> using namespace std; class BRTree; class BRTreeNode{ private: friend class BRTree; int key; bool color; int size; BRTreeNode *left; BRTreeNode *right; BRTreeNode *parent; public: //创建一个默认构造函数 BRTreeNode():key(-1),color(0),size(0),

编程算法 - 最小的k个数 红黑树 代码(C++)

最小的k个数 红黑树 代码(C++) 本文地址: http://blog.csdn.net/caroline_wendy 题目: 输入n个整数, 找出其中的最小k个数. 使用红黑树(multiset), 每次替换最大的值, 依次迭代. 时间复杂度: O(nlogk). 代码: /* * main.cpp * * Created on: 2014年6月29日 * Author: wang */ #include <iostream> #include <vector> #includ

算法导论 第6章 堆排序

堆数据结构实际上是一种数组对象,是以数组的形式存储的,但是它可以被视为一颗完全二叉树,因此又叫二叉堆.堆分为以下两种类型: 大顶堆:父结点的值不小于其子结点的值,堆顶元素最大 小顶堆:父结点的值不大于其子结点的值,堆顶元素最小 堆排序的时间复杂度跟合并排序一样,都是O(nlgn),但是合并排序不是原地排序(原地排序:在排序过程中,只有常数个元素是保存在数组以外的空间),合并排序的所有元素都被拷贝到另外的数组空间中去,而堆排序是一个原地排序算法. 1.在堆排序中,我们通常使用大顶堆来实现,由于堆在

算法导论 第6章 堆排序(简单选择排序、堆排序)

堆数据结构实际上是一种数组对象,是以数组的形式存储的,可是它能够被视为一颗全然二叉树,因此又叫二叉堆.堆分为下面两种类型: 大顶堆:父结点的值不小于其子结点的值,堆顶元素最大 小顶堆:父结点的值不大于其子结点的值,堆顶元素最小 堆排序的时间复杂度跟合并排序一样,都是O(nlgn),可是合并排序不是原地排序(原地排序:在排序过程中,仅仅有常数个元素是保存在数组以外的空间),合并排序的全部元素都被复制到另外的数组空间中去,而堆排序是一个原地排序算法. 1.在堆排序中,我们通常使用大顶堆来实现,因为堆

算法导论 第8章 线性时间排序

合并排序和堆排序的时间复杂度为O(nlgn),插入排序和冒泡排序的时间复杂度为O(n^2),快速排序的时间复杂度在平均情况下是O(nlgn),这些排序算法都是通过对元素进行相互比较从而确定顺序的,因此都叫比较排序. 比较排序可以看做是决策树(一个满二叉树),因为每一次比较都是一个分支.n个元素的序列,其排序的结果有 n! 种可能(n个元素的全排),所以这个决策树有 n! 个叶子结点,假设树的高度为h,则有:n! <= 2^h,所以h >= lg(n!) = Ω(nlgn).一次比较排序就是从决