1. 红⿊树的概念
红⿊树是⼀棵⼆叉搜索树,他的每个结点增加⼀个存储位来表⽰结点的颜⾊,可以是红⾊或者⿊⾊。 通过对任何⼀条从根到叶⼦的路径上各个结点的颜⾊进⾏约束,红⿊树确保没有⼀条路径会⽐其他路 径⻓出2倍,因⽽是接近平衡的。(但红黑树不是绝对平衡的)
1.1 红⿊树的规则:
1. 每个结点不是红⾊就是⿊⾊
2. 根结点是⿊⾊的
3. 如果⼀个结点是红⾊的,则它的两个孩⼦结点必须是⿊⾊的,也就是说任意⼀条路径不会有连续的 红⾊结点。
4. 对于任意⼀个结点,从该结点到其所有NULL结点的简单路径上,均包含相同数量的⿊⾊结点 说明:《算法导论》等书籍上补充了⼀条每个叶⼦结点(NIL)都是⿊⾊的规则。他这⾥所指的叶⼦结点 不是传统的意义上的叶⼦结点,⽽是我们说的空结点,有些书籍上也把NIL叫做外部结点。NIL是为了 ⽅便准确的标识出所有路径,《算法导论》在后续讲解实现的细节中也忽略了NIL结点,所以我们知道 ⼀下这个概念即可。
1.2 思考⼀下,红⿊树如何确保最⻓路径不超过最短路径的2倍的?
• 由规则4可知,从根到NULL结点的每条路径都有相同数量的⿊⾊结点,所以极端场景下,最短路径 就就是全是⿊⾊结点的路径,假设最短路径⻓度为bh(blackheight)。
• 由规则2和规则3可知,任意⼀条路径不会有连续的红⾊结点,所以极端场景下,最⻓的路径就是⼀ ⿊⼀红间隔组成,那么最⻓路径的⻓度为2*bh。
• 综合红⿊树的4点规则⽽⾔,理论上的全⿊最短路径和⼀⿊⼀红的最⻓路径并不是在每棵红⿊树都 存在的。假设任意⼀条从根到NULL结点路径的⻓度为x,那么bh
1.3 红⿊树的效率:
假设N是红⿊树树中结点数量,h最短路径的⻓度,那么由此推出 ,也就是意味着红⿊树增删查改最坏也就是⾛最⻓路径 ,那么时间复杂度还是 :
2^ h − 1< N < 2^ 2∗h − 1
h ≈ logN 2 ∗ logN O(logN)
红⿊树的表达相对AVL树要抽象⼀些,AVL树通过⾼度差直观的控制了平衡。红⿊树通过4条规则的颜 ⾊约束,间接的实现了近似平衡,他们效率都是同⼀档次,但是相对⽽⾔,插⼊相同数量的结点,红 ⿊树的旋转次数是更少的,因为他对平衡的控制没那么严格。
2. 红⿊树的实现
2.1 红⿊树的结构
//单个节点,枚举表示颜色
enum Color
{
Balck
, Red
};
//其余的参考没有平衡因子的avl树的结构
template<class K,class V>
//把这个设计成struct,全部都是public
struct RBTreeNode
{
//参数
pair<K, V> _kv;
RBTreeNode<K,V>* _left;
RBTreeNode<K,V>* _right;
RBTreeNode<K,V>* _parent;
Color _color;
//初始化
RBTreeNode(const pair<K,V>& kv)
:_kv(kv)
,_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
{}
};
template<class K,class V>
class RBTree
{
public:
using Node = RBTreeNode<K,V>;
private:
Node* _root = nullptr;
};
2.2 红⿊树的插⼊
2.2.1 红⿊树树插⼊⼀个值的⼤概过程
1. 插⼊⼀个值按⼆叉搜索树规则进⾏插⼊,插⼊后我们只需要观察是否符合红⿊树的4条规则。
2. 如果是空树插⼊,新增结点是⿊⾊结点。如果是⾮空树插⼊,新增结点必须红⾊结点,因为⾮空树 插⼊,新增⿊⾊结点就破坏了规则4,规则4是很难维护的。
3. ⾮空树插⼊后,新增结点必须红⾊结点,如果⽗亲结点是⿊⾊的,则没有违反任何规则,插⼊结束
4. ⾮空树插⼊后,新增结点必须红⾊结点,如果⽗亲结点是红⾊的,则违反规则3。进⼀步分析,c是 红⾊,p为红,g必为⿊,这三个颜⾊都固定了,关键的变化看u的情况,需要根据u分为以下⼏种 情况分别处理。
2.2.2 情况1:变⾊(c为当前节点,p为父节点,u为叔叔节点,g为祖父节点)
“叔叔存在且叔叔为红”
c为红,p为红,g为⿊,u存在且为红,则将p和u变⿊,g变红。在把g当做新的c,继续往上更新。
分析:因为p和u都是红⾊,g是⿊⾊,把p和u变⿊,左边⼦树路径各增加⼀个⿊⾊结点,g再变红,相 当于保持g所在⼦树的⿊⾊结点的数量不变,同时解决了c和p连续红⾊结点的问题,需要继续往上更新 是因为,g是红⾊,如果g的⽗亲还是红⾊,那么就还需要继续处理;如果g的⽗亲是⿊⾊,则处理结束 了;如果g就是整棵树的根,再把g变回⿊⾊。情况1只变⾊,不旋转。所以⽆论c是p的左还是右,p是g的左还是右,都是上⾯的变⾊处理⽅式。
if (uncle && uncle->_color == Red)
{
parent->_color = Balck;
uncle->_color = Balck;
grandparnet->_color = Red;
//更新当前节点和父节点,继续向上检查
cur = grandparnet;
parent = cur->_parent;
}
总结:这里就是指,当叔叔节点存在,且为红的时候,把父节点和叔叔节点变成黑,再把祖父节点变成红,更新当前节点和父节点,继续向上检查
2.2.3 情况2:单旋+变⾊
“叔叔不存在或为黑”的第一种情况
c为红,p为红,g为⿊,u不存在或者u存在且为⿊,u不存在,则c⼀定是新增结点,u存在且为⿊,则 c⼀定不是新增,c之前是⿊⾊的,是在c的⼦树中插⼊,符合情况1,变⾊将c从⿊⾊变成红⾊,更新上 来的。 分析:p必须变⿊,才能解决,连续红⾊结点的问题,u不存在或者是⿊⾊的,这⾥单纯的变⾊⽆法解 决问题,需要旋转+变⾊。
比如说:
当parent是g的左子树的时候,需要对g节点进行右单旋,同时把p变成黑色,g变成红色
if (cur == parent->_left)
{
// g
// p u
// c
//对g进行右单旋 ,然后把p变成黑,g变成红
RotateR(grandparnet);
parent->_color = Balck;
grandparnet->_color = Red;
}
当parent是g的右子树的时候,同理,对g进行左单旋,同时把p变成黑色,g变成红色
else
{
/* g
u p
c
直接对g进行左单旋,然后把p变成黑,把g变成红*/
RotateL(grandparnet);
grandparnet->_color = Red;
parent->_color = Balck;
}
2.2.4 情况2:双旋+变⾊
“叔叔不存在或为黑”的第二种情况
比如:
当p为g的左子树,c为p的右子树时, 先把p进行左单旋变成图3,在对g进行右单旋,把c变黑,把g变红即可
else if (cur == parent->_right)
{
// g
// p u
// c
//先对p进行左单旋,再对g进行右单旋,把c变成黑,g变成红
RotateL(parent);
RotateR(grandparnet);
cur->_color = Balck;
grandparnet->_color = Red;
}
同理,当p为g的右子树,c为p的左子树时, 先把p进行右单旋变成图3,在对g进行左单旋,把c变黑,把g变红即可
if (cur == parent->_left)
{
/* g
u p
c
先对p进行右单旋,在对g进行左单旋,然后把c变成黑,把g变成红*/
RotateR(parent);
RotateL(grandparnet);
cur->_color = Balck;
grandparnet->_color = Red;
}
2.3 红⿊树的插⼊代码实现
bool Insert(const pair<K, V>& kv)
{
if (_root == nullptr)
{
Node* root = new Node(kv);
_root = root;
_root->_color = Balck;
return true;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (kv.first > cur->_kv.first)
{
parent = cur;
cur = cur->_right;
}
else if (kv.first < cur->_kv.first)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
cur = new Node(kv);
cur->_color = Red;
if (kv.first < parent->_kv.first)
{
parent->_left = cur;
cur->_parent = parent;
}
else
{
parent->_right = cur;
cur->_parent = parent;
}
while (parent && parent->_color == Red)
{
Node* grandparnet = parent->_parent;
if (parent == grandparnet->_left)
{
Node* uncle = grandparnet->_right;
if (uncle && uncle->_color == Red)
{
parent->_color = Balck;
uncle->_color = Balck;
grandparnet->_color = Red;
cur = grandparnet;
parent = cur->_parent;
}
else
{
if (cur == parent->_left)
{
// g
// p u
// c
//对g进行右单旋 ,然后把p变成黑,g变成红
RotateR(grandparnet);
parent->_color = Balck;
grandparnet->_color = Red;
}
else if (cur == parent->_right)
{
// g
// p u
// c
//先对p进行左单旋,再对g进行右单旋,把c变成黑,g变成红
RotateL(parent);
RotateR(grandparnet);
cur->_color = Balck;
grandparnet->_color = Red;
}
break;
}
}
else if (parent == grandparnet->_right)
{
Node* uncle = grandparnet->_left;
if (uncle && uncle->_color == Red)
{
uncle->_color = Balck;
parent->_color = Balck;
grandparnet->_color = Red;
cur = grandparnet;
parent = cur->_parent;
}
else
{
if (cur == parent->_left)
{
/* g
u p
c
先对p进行右单旋,在对g进行左单旋,然后把c变成黑,把g变成红*/
RotateR(parent);
RotateL(grandparnet);
cur->_color = Balck;
grandparnet->_color = Red;
}
else
{
/* g
u p
c
直接对g进行左单旋,然后把p变成黑,把g变成红*/
RotateL(grandparnet);
grandparnet->_color = Red;
parent->_color = Balck;
}
break;
}
}
}
_root->_color = Balck;
return true;
}
2.4 红⿊树的查找
//查找节点
Node* Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (key > cur->_kv.first)
{
cur = cur->_right;
}
else if (key < cur->_kv.first)
{
cur = cur->_left;
}
else
{
return cur;
}
}
return nullptr;
}
2.5 红⿊树的验证
这⾥获取最⻓路径和最短路径,检查最
⻓路径不超过最短路径的2倍是不可⾏的,因为就算满⾜这个条 件,红⿊树也可能颜⾊不满⾜规则,当前暂时没出问题,后续继续插⼊还是会出问题的。所以我们还 是去检查4点规则,满⾜这4点规则,⼀定能保证最⻓路径不超过最短路径的2倍。
1. 规则1枚举颜⾊类型,天然实现保证了颜⾊不是⿊⾊就是红⾊。
2. 规则2直接检查根即可
3. 规则3前序遍历检查,遇到红⾊结点查孩⼦不太⽅便,因为孩⼦有两个,且不⼀定存在,反过来检 查⽗亲的颜⾊就⽅便多了。
4. 规则4前序遍历,遍历过程中⽤形参记录跟到当前结点的blackNum(⿊⾊结点数量),前序遍历遇到 ⿊⾊结点就++blackNum,⾛到空就计算出了⼀条路径的⿊⾊结点数量。再任意⼀条路径⿊⾊结点 数量作为参考值,依次⽐较即可。
bool Check(Node* root, int blackNum, const int refNum)
{
if (root == nullptr)
{
// 前序遍历⾛到空时,意味着⼀条路径⾛完了
//cout << blackNum << " " << refNum << endl;
if (refNum != blackNum)
{
cout << "存在黑色结点不相等的路径" << endl;
return false;
}
return true;
}
// 检查孩⼦不太⽅便,因为孩⼦有两个,且不⼀定存在,反过来检查⽗亲就⽅便多了
if (root->_color == Red && root->_parent->_color == Red)
{
cout << root->_kv.first << "存在连续的红⾊结点" << endl;
return false;
}
if (root->_color == Balck)
{
blackNum++;
}
return Check(root->_left, blackNum, refNum)
&& Check(root->_right, blackNum, refNum);
}
bool IsBalance()
{
if (_root == nullptr)
return true;
if (_root->_color == Red)
return false;
// 参考值
int refNum = 0;
Node* cur = _root;
while (cur)
{
if (cur->_color == Balck)
{
++refNum;
}
cur = cur->_left;
}
return Check(_root, 0, refNum);
}
2.6红黑树完整代码
RBTree.h
#include<iostream>
using namespace std;
//单个节点
enum Color
{
Balck
, Red
};
template<class K,class V>
//把这个设计成struct,全部都是public
struct RBTreeNode
{
//参数
pair<K, V> _kv;
RBTreeNode<K,V>* _left;
RBTreeNode<K,V>* _right;
RBTreeNode<K,V>* _parent;
Color _color;
//初始化
RBTreeNode(const pair<K,V>& kv)
:_kv(kv)
,_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
{}
};
template<class K,class V>
class RBTree
{
public:
using Node = RBTreeNode<K,V>;
//红黑树的插入
bool Insert(const pair<K, V>& kv)
{
if (_root == nullptr)
{
Node* root = new Node(kv);
_root = root;
_root->_color = Balck;
return true;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (kv.first > cur->_kv.first)
{
parent = cur;
cur = cur->_right;
}
else if (kv.first < cur->_kv.first)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
cur = new Node(kv);
cur->_color = Red;
if (kv.first < parent->_kv.first)
{
parent->_left = cur;
cur->_parent = parent;
}
else
{
parent->_right = cur;
cur->_parent = parent;
}
while (parent && parent->_color == Red)
{
Node* grandparnet = parent->_parent;
if (parent == grandparnet->_left)
{
Node* uncle = grandparnet->_right;
if (uncle && uncle->_color == Red)
{
parent->_color = Balck;
uncle->_color = Balck;
grandparnet->_color = Red;
cur = grandparnet;
parent = cur->_parent;
}
else
{
if (cur == parent->_left)
{
// g
// p u
// c
//对g进行右单旋 ,然后把p变成黑,g变成红
RotateR(grandparnet);
parent->_color = Balck;
grandparnet->_color = Red;
}
else if (cur == parent->_right)
{
// g
// p u
// c
//先对p进行左单旋,再对g进行右单旋,把c变成黑,g变成红
RotateL(parent);
RotateR(grandparnet);
cur->_color = Balck;
grandparnet->_color = Red;
}
break;
}
}
else if (parent == grandparnet->_right)
{
Node* uncle = grandparnet->_left;
if (uncle && uncle->_color == Red)
{
uncle->_color = Balck;
parent->_color = Balck;
grandparnet->_color = Red;
cur = grandparnet;
parent = cur->_parent;
}
else
{
if (cur == parent->_left)
{
/* g
u p
c
先对p进行右单旋,在对g进行左单旋,然后把c变成黑,把g变成红*/
RotateR(parent);
RotateL(grandparnet);
cur->_color = Balck;
grandparnet->_color = Red;
}
else
{
/* g
u p
c
直接对g进行左单旋,然后把p变成黑,把g变成红*/
RotateL(grandparnet);
grandparnet->_color = Red;
parent->_color = Balck;
}
break;
}
}
}
_root->_color = Balck;
return true;
}
void Inorder()
{
_Inorder(_root);
}
//查找节点
Node* Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (key > cur->_kv.first)
{
cur = cur->_right;
}
else if (key < cur->_kv.first)
{
cur = cur->_left;
}
else
{
return cur;
}
}
return nullptr;
}
bool Check(Node* root, int blackNum, const int refNum)
{
if (root == nullptr)
{
// 前序遍历⾛到空时,意味着⼀条路径⾛完了
//cout << blackNum << " " << refNum << endl;
if (refNum != blackNum)
{
cout << "存在黑色结点不相等的路径" << endl;
return false;
}
return true;
}
// 检查孩⼦不太⽅便,因为孩⼦有两个,且不⼀定存在,反过来检查⽗亲就⽅便多了
if (root->_color == Red && root->_parent->_color == Red)
{
cout << root->_kv.first << "存在连续的红⾊结点" << endl;
return false;
}
if (root->_color == Balck)
{
blackNum++;
}
return Check(root->_left, blackNum, refNum)
&& Check(root->_right, blackNum, refNum);
}
bool IsBalance()
{
if (_root == nullptr)
return true;
if (_root->_color == Red)
return false;
// 参考值
int refNum = 0;
Node* cur = _root;
while (cur)
{
if (cur->_color == Balck)
{
++refNum;
}
cur = cur->_left;
}
return Check(_root, 0, refNum);
}
private:
void _Inorder(Node* root)
{
if (root == nullptr)
{
return;
}
_Inorder(root->_left);
cout << root->_kv.first<<" ";
_Inorder(root->_right);
}
void RotateR(Node* parent)
{
Node* child_left = parent->_left;
Node* child_left_right = child_left->_right;
if (child_left_right != nullptr)
{
child_left_right->_parent = parent;
}
parent->_left = child_left_right;
Node* parent_parent = parent->_parent;
parent->_parent = child_left;
child_left->_right = parent;
if (parent_parent == nullptr)
{
_root = child_left;
child_left->_parent = nullptr;
}
else
{
if (parent == parent_parent->_left)
{
parent_parent->_left = child_left;
child_left->_parent = parent_parent;
}
else
{
parent_parent->_right = child_left;
child_left->_parent = parent_parent;
}
}
}
void RotateL(Node* parent)
{
Node* pR = parent->_right;
Node* pRL = pR->_left;
parent->_right = pRL;
if (pRL)
{
pRL->_parent = parent;
}
pR->_left = parent;
Node* parentparent = parent->_parent;
parent->_parent = pR;
if (parentparent == nullptr)
{
_root = pR;
pR->_parent = nullptr;
}
else
{
if (parentparent->_left == parent)
{
parentparent->_left = pR;
pR->_parent = parentparent;
}
else
{
parentparent->_right = pR;
pR->_parent = parentparent;
}
}
}
private:
Node* _root = nullptr;
};
Test.cpp
#include"RBTree.h"
void TestRBTree1()
{
RBTree<int, int> t;
// 常规的测试用例
//int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
// 特殊的带有双旋场景的测试用例
int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
for (auto e : a)
{
t.Insert({ e, e });
}
t.Inorder();
cout << endl;
cout << t.IsBalance() << endl;
}
int main()
{
TestRBTree1();
return 0;
}