数据结构之红黑树(RBTree)图解

红黑树是一棵二叉搜索树,它在每个节点上增加了一个存储位来表示节点的颜色,可以是RedBlack

通过对任何一条从根到叶子节点简单路径上的颜色来约束树的高度,红黑树保证最长路径不超过最短路径的两倍,因而近似于平衡。

红黑树是满足下面红黑性质的二叉搜索树:

1. 每个节点,不是红色就是黑色的

2. 根节点是黑色的

3. 如果一个节点是红色的,则它的两个子节点是黑色的(不存在连续的红色节点)

4. 对每个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点。

 

思考:为什么满足上面的颜色约束性质,红黑树能保证最长路径不超过最短路径的两倍?

  最短的路径上节点的颜色全部都为黑色;最长的路径则为黑红交叉的路径,其上有与最短路径的黑节点数目相同的黑节点数和红节点数目。所以我们按照红黑树性质所建立的红黑树的最长路径必然不会超过最短路径的两倍!

建立红黑树的节点类:

插入的新节点默认是红色的。原因是:插入黑节点必然会影响所有路径都含有相同数目的黑色节点这一原则,较难维护!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
enum  Color
{
     RED,
     BLACK
};
 
template < class  K, class  V>
struct  RBTreeNode
{
     K _key;
     V _value;
     Color _color;    //颜色
     RBTreeNode<K, V>* _left;       //指向左孩子的指针
     RBTreeNode<K, V>* _right;  //指向右孩子的指针
     RBTreeNode<K, V>* _parent;     //指向父节点的指针
 
     RBTreeNode( const  K& key=K(),  const  V&value=V())
         :_key(key)
         , _value(value)
         , _color(RED)
         , _left(NULL)
         , _right(NULL)
         , _parent(NULL)
     {}
};

  红黑树需要变色或利用旋转来降低高度的几种情况:

图注:g代表grandfather祖父节点;p代表parent父亲结点;u代表uncle叔叔节点;cur代表当前节点

一、父节点是祖父节点的左孩子

1.uncle的颜色是红色

①当前节点cur是parent的左孩子

②当前节点cur是parent的右孩子

 

2.uncle的颜色是黑色 或者 uncle为NULL

①cur是parent的左孩子,右单旋

②cur是parent的右孩子,先左后右双旋

二、父节点是祖父节点的右孩子

1.uncle的颜色是红色

①cur是parent的右孩子

②cur是parent的左孩子

2.uncle的颜色是黑色 或者 uncle为NULL

①cur是parent的右孩子

②cur是parent的左孩子

插入节点:

  1. 首先,要找到插入该节点的位置,找到后就插入节点
  2. 然后,对红黑树的节点颜色的合法性进行检查,并根据检查结果进行变色或者旋转。

基于以上的情况,红黑树利用模板类封装的插入函数算法就完成了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
template < class  K,  class  V>
bool  RBTree<K, V>::Insert( const  K& key,  const  V& value)
{
     if  (_root == NULL)
     {
         _root =  new  RBTreeNode<K, V>(key, value);
         _root->_color = BLACK;
         return  true ;
     }
     //  找位置
     RBTreeNode<K, V>* cur = _root;
     RBTreeNode<K, V>* parent = NULL;
     while  (cur)
     {
         if  (cur->_key > key)
         {
             parent = cur;
             cur = cur->_left;
         }
         else  if  (cur->_key < key)
         {
             parent = cur;
             cur = cur->_right;
         }
         else
         {
             return  false ;
         }
     }
     //插入
     cur =  new  RBTreeNode<K, V>(key, value);
     cur->_parent = parent;
     if  (parent->_key > key)
         parent->_left = cur;
     else  if  (parent->_key < key)
         parent->_right = cur;
     
     //检查颜色分配是否满足要求
     while  (parent&&parent->_color==RED)
     {
         RBTreeNode<K, V>* grandfather = parent->_parent;
         if  (parent == grandfather->_left)
         {
             RBTreeNode<K, V>* uncle = grandfather->_right;
             if  (uncle&&uncle->_color == RED)
             {    //第一种情况  变色
                 grandfather->_color = RED;
                 parent->_color =BLACK;
                 uncle->_color = BLACK;
                 
                 cur = grandfather;
                 parent = grandfather->_parent;
             }
             else  if ( (uncle&&uncle->_color==BLACK)||uncle==NULL)
             {  
                 if  (cur == parent->_left)
                 { //第二种情况 右单旋        cur必然有黑色孩子
                     parent->_color = BLACK;
                     grandfather->_color = RED;
                     RotateR(grandfather);
                 }
                 else
                 { //第三种情况 左右双旋
                     RotateL(parent);
                     parent->_color = BLACK;
                     grandfather->_color = RED;
                     RotateR(grandfather);
                 }
                 break ;
             }
         }
         else  if  (parent == grandfather->_right)
         {
             RBTreeNode<K, V>* uncle = grandfather->_left;
             if  (uncle&&uncle->_color == RED)
             { //第一种情况 变色
                 grandfather->_color = RED;
                 parent->_color = BLACK;
                 uncle->_color = BLACK;
                 
                 cur = grandfather;
                 parent = cur->_parent;
             }
             else  if ( (uncle&&uncle->_color == BLACK)||uncle==NULL)
             { //第二种情况 左单旋 cur必然有黑孩子
                 if  (cur == parent->_right)
                 {
                     parent->_color = BLACK;
                     grandfather->_color = RED;
                     RotateL(grandfather);
                 }
                 else  if  (cur==parent->_left)
                 { //第三种情况 右左双旋
                     RotateR(parent);
                     parent->_color = BLACK;
                     grandfather->_color = RED;  
                     RotateL(grandfather);
                 }
                 break ;
             }
         }
     }
     _root->_color = BLACK;
     return  true ;
}

  插入完成之后,我们无法直观的看出红黑树的节点颜色是否合法,也无法直观的看出每条路径的黑色节点数目是否相同。

所以,这里实现两个函数,方便检验红黑树的合法性。

  • 红黑树每条路径的黑色节点数目都相同,所以随意遍历一条路径,计算这条路上的黑色节点的数目。以该数据为标杆,和其他路径的黑色节点数目作比较,判断是否都相同。
  • 如果当前节点是红颜色并且它有父节点,那么再判断父节点的颜色是否也是红色,这样就能判断该树是否满足连续两个节点不能同时为红色这一性质。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//检验红黑树的合法性
template < class  K,  class  V>
bool  RBTree<K, V>::Check()
{
     //统计红黑树每条路径上黑色节点的数量
     int  blackNum = 0;
     RBTreeNode<K, V>* cur = _root;
     while  (cur)
     {
         if  (cur->_color == BLACK)
             blackNum++;
         cur = cur->_left;
     }
     int  CBNum = 0;
     return  _Check(_root,blackNum,CBNum);
}
 
// 递归辅助
template < class  K,  class  V>
bool  RBTree<K, V>::_Check(RBTreeNode<K, V>* root,  int  blackNum,  int  CBNum)
{
     if  (root == NULL)
         return  true ;
     if  (root->_color == BLACK)
     {
         CBNum++;
         if  (root->_left == NULL&&root->_right == NULL)
         {    //走到了叶子节点 将该条路径上的黑色节点数量与之前统计的黑色节点数量做比较
             if  (blackNum == CBNum)
             {
                 return  true ;
             }
             else
             {
                 cout <<  "叶子节点为"  << root->_key <<  "路径的黑色节点数目与最左侧支路的黑色节点数目不相等 !"  << endl;
                 return  false ;
             }
         }
     }
     else  if  (root->_parent&&root->_parent->_color == RED)
     { //判断是否存在连续的两个红色节点
         cout << root->_parent->_key <<  " 和 "  << root->_key <<  " 为两个连续的红色节点"  << endl;
         return  false ;
     }
     //递归检验子路
     return  _Check(root->_left, blackNum, CBNum) && _Check(root->_right, blackNum, CBNum);
}

  红黑树和AVL树的比较

红黑树和AVL树都是高效的平衡二叉树,增删查改的时间复杂度都是O(lg(N)) 红黑树的不追求完全平衡,保证最长路径不超过最短路径的2倍,相对而言,降低了旋转的要求,所以性能会优于AVL树,所以实际运用 中红黑树更多。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值