红黑树的部分实现(C++)
1. 红黑树的概念
红黑树是⼀棵⼆叉搜索树,他的每个结点增加⼀个元素来表示结点的颜⾊,可以是红色或者黑色。 通过对任何⼀条从根到叶子的路径上各个结点的颜⾊进行一定规则的约束,确保没有⼀条路径会比其他路径长出2倍,因次是接近平衡的。
1.1 红黑树的规则
1)每个结点不是红色就是黑色
2)根结点是黑色的
3)如果⼀个结点是红色的,则它的两个孩子结点必须是黑色的,也就是说任意⼀条路径不会有连续的红色结点。
4)对于任意⼀个结点,从该结点到其所有NULL结点的简单路径上,均包含相同数量的黑色结
例如:
1.2 红黑树最长路径不超过最短路径的2倍
由规则4可知,从根到NULL结点的每条路径都有相同数量的黑色结点,所以极端场景下,最短路径 就是全是⿊色结点的路径。 由规则2和规则3可知,任意⼀条路径不会有连续的红色结点,所以极端场景下,最长的路径就是⼀⿊⼀红间隔组成。
综上所述,理论上的全黑最短路径和⼀黑⼀红的最长路径并不是在每棵红黑树都存在的。
1.3 红黑树的效率
假设N是红⿊树树中结点数量,h最短路径的长度,那么2^h-1<=N<2^2*h-1,由此推出,h约等于logN,也就是意味着红⿊树增删查改最坏也就是⾛最长路径 2 ∗logN 。那么时间复杂度还是O(logN)。(和之前的AVL树的时间复杂度一样)
红黑树通过4条规则的颜色约束,间接的实现了近似平衡,他们效率都差不多,但是,插入相同数量的结点,红黑树的旋转次数是更少的,因为他对平衡的控制没那么严格。
2. 红黑树的实现
2.1 红黑树的结构
#pragma once
#include<iostream>
using namespace std;
enum Color
{
RED,
BLACK
};
template<class K,class V>
struct RBTreeNode
{
pair<K, V> _kv;
RBTreeNode<K, V>* _parent;
RBTreeNode<K, V>* _left;
RBTreeNode<K, V>* _right;
Color _col;
RBTreeNode(const pair<K, V>& kv)
:_kv(kv)
,_parent(nullptr)
,_left(nullptr)
,_right(nullptr)
{}
};
template<class K,class V>
class RBTree
{
typedef RBTreeNode<K, V> Node;
public:
private:
Node* _root = nullptr;
};
红黑树结点包含左孩子结点,右孩子结点,双亲结点,结点颜色,以及存储的元素即pair<K,V>。
2.2 红黑树的插入
2.2.1 红黑树树插入的逻辑
a) 首先明确一点,非空树添加的结点时,必须为红色,不能为黑色,要不然会破坏规则4。
b)如果是空树插入,新增结点是黑色结点。如果是非空树插入,新增结点必须红⾊结点,因为非空树插入,新增黑色结点就破坏了规则4,规则4是很难维护的。
c)非空树插入后,新增结点必须红色结点,如果父亲结点是黑⾊的,则没有违反任何规则,插入结束。
d)非空树插入后,新增结点必须红色结点,如果父亲结点是红色的,则违反规则3。
c是红色,p为红,g必为黑(可以根据上述规则确定),这三个颜色都固定了,关键的变化看u的情况,需要根据u分为以下几种情况分别处理。
2.2.2 情况1:变色
(c为红,p为红,g为黑,u存在且为红)
c为红,p为红,g为黑,u存在且为红,则将p和u变黑,g变红。再将g当做新的c,继续往上更新。
实现代码:
while (parent && parent->_col == RED)
{
Node* grandfather = parent->_parent;
if (parent == grandfather->_left)
{
Node* uncle = grandfather->_right;
if (uncle && uncle->_col == RED)
{
grandfather->_col = RED;
parent->_col = BLACK;
uncle->_col = BLACK;
cur = grandfather;
parent = cur->_parent;
}
else
{
.....
}
}
else
{
.....
}
}
2.2.3 情况2:单旋+变色
(c为红,p为红,g为黑,u不存在或者u存在且为黑,且c为p的左孩子结点)
u不存在的情况:
u存在的情况:
c为红,p为红,g为黑,u不存在或者u存在且为黑,u不存在,则c⼀定是新增结点,u存在且为⿊,则 c⼀定不是新增,c之前是黑色的,是在c的⼦树中插入,符合情况1,变色将c从黑色变成红色,更新上来的。
代码实现:
while (parent && parent->_col == RED)
{
Node* grandfather = parent->_parent;
if (parent == grandfather->_left)
{
Node* uncle = grandfather->_right;
if (uncle && uncle->_col == RED)
{
grandfather->_col = RED;
parent->_col = BLACK;
uncle->_col = BLACK;
cur = grandfather;
parent = cur->_parent;
}
else
{
if (cur == parent->_left)
{
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
......
}
break;
}
}
else
{
.....
}
}
2.2.4 情况3:双旋+变色
(c为红,p为红,g为黑,u不存在或者u存在且为黑,且c为p的右孩子结点)
p是g的左,c是p的右,那么先以p为旋转点进行左单旋,再以g为旋转点进行右单旋,再把c变 黑,g变红即可。c变成课这颗树新的根,这样⼦树黑色结点的数量不变,没有连续的红⾊结点了,且不需要往上更新,因为c的父亲是黑色还是红色或者空都不违反规则。
代码实现:
while (parent && parent->_col == RED)
{
Node* grandfather = parent->_parent;
if (parent == grandfather->_left)
{
Node* uncle = grandfather->_right;
if (uncle && uncle->_col == RED)
{
grandfather->_col = RED;
parent->_col = BLACK;
uncle->_col = BLACK;
cur = grandfather;
parent = cur->_parent;
}
else
{
if (cur == parent->_left)
{
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
else
{
......
}
}
2.3 红黑树插入代码整体实现
这里还要注意,根节点必须为黑色,这里是在最后统一赋值为黑色,中间向上更新过程中根可能为红色,在最后处理。(上述三种情况当在右子树时逻辑是一样的,这里不做赘述,其次左右选转的概念以及实现可以参考之前的文章AVL树中有详细讲解)。
bool insert(const pair<K, V>& kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
_root->_col = BLACK;
return true;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (kv.first < cur->_kv.first)
{
parent = cur;
cur = cur->_left;
}
else if (kv.first > cur->_kv.first)
{
parent = cur;
cur = cur->_right;
}
else
{
return false;
}
}
cur = new Node(kv);
cur->_col = RED;
if (parent->_kv.first>kv.first)
{
parent->_left = cur;
}
else
{
parent->_right = cur;
}
cur->_parent = parent;
while (parent && parent->_col == RED)
{
Node* grandfather = parent->_parent;
if (parent == grandfather->_left)
{
Node* uncle = grandfather->_right;
if (uncle && uncle->_col == RED)
{
grandfather->_col = RED;
parent->_col = BLACK;
uncle->_col = BLACK;
cur = grandfather;
parent = cur->_parent;
}
else
{
if (cur == parent->_left)
{
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
else
{
Node* uncle = grandfather->_left;
if (uncle && uncle->_col == RED)
{
grandfather->_col = RED;
parent->_col = BLACK;
uncle->_col = BLACK;
cur = grandfather;
parent = cur->_parent;
}
else
{
if (cur == parent->_right)
{
RotateL(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
RotateR(parent);
RotateL(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
}
_root->_col = BLACK;
return true;
}
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
Node* parentParent = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
if (parentParent == nullptr)
{
_root = subL;
subL->_parent = nullptr;
}
else
{
if (parent == parentParent->_left)
{
parentParent->_left = subL;
}
else
{
parentParent->_right = subL;
}
subL->_parent = parentParent;
}
}
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
Node* parentParent = parent->_parent;
subR->_left = parent;
parent->_parent = subR;
if (parentParent == nullptr)
{
_root = subR;
subR->_parent = nullptr;
}
else
{
if (parent == parentParent->_left)
{
parentParent->_left = subR;
}
else
{
parentParent->_right = subR;
}
subR->_parent = parentParent;
}
}
2.4红黑树的查找
查找和搜索二叉树的查找逻辑时一样的。
Node* Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (cur->_kv.first < key)
{
cur = cur->_right;
}
else if (cur->_kv.first > key)
{
cur = cur->_left;
}
else
{
return cur;
}
}
return nullptr;
}
2.5红黑树的验证
a)规则1枚举颜⾊类型,实现时已经保证了颜色不是黑色就是红色。
b)规则2直接检查根即可
c)规则3前序遍历检查,遇到红色结点查孩子不太方便,因为孩子有两个,且不⼀定存在,反过来检查父亲的颜色就比较方便。
d)规则4前序遍历,遍历过程中用形参记录跟到当前结点的blackNum(黑色结点数量),前序遍历遇到黑色结点就++blackNum,⾛到空就计算出了⼀条路径的黑色结点数量。再任意⼀条路径黑色结点数量作为参考值,依次比较即可。
bool Check(Node* root, int blacknum, const int refnum)
{
if (root == nullptr)
{
if (blacknum == refnum)
return true;
else
{
cout << "存在黑色节点的数量不相等的路径" << endl;
return false;
}
}
if (root->_col == RED && root->_parent && root->_parent->_col == RED)
{
cout << "存在连续的红色结点" << endl;
return false;
}
if (root->_col == BLACK)
{
blacknum++;
}
return Check(root->_left, blacknum, refnum) && Check(root->_right, blacknum, refnum);
}
bool IsBalanceTree()
{
if (_root == nullptr)
return true;
if (_root->_col == RED)
return false;
Node* cur = _root;
int refnum = 0;
while (cur)
{
if (cur->_col == BLACK)
{
++refnum;
}
cur = cur->_left;
}
return Check(_root, 0, refnum);
}
2.5 示例
void TestRBTree1()
{
RBTree<int, int> t;
// 带有双旋测试用例
int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14,18};
for (auto e : a)
{
t.insert({ e, e });
}
t.Inoder();
cout << t.IsBalanceTree() << endl;
}
结果:
3.整体代码
#include<iostream>
using namespace std;
enum Color
{
RED,
BLACK
};
template<class K,class V>
struct RBTreeNode
{
pair<K, V> _kv;
RBTreeNode<K, V>* _parent;
RBTreeNode<K, V>* _left;
RBTreeNode<K, V>* _right;
Color _col;
RBTreeNode(const pair<K, V>& kv)
:_kv(kv)
,_parent(nullptr)
,_left(nullptr)
,_right(nullptr)
{}
};
template<class K,class V>
class RBTree
{
typedef RBTreeNode<K, V> Node;
public:
bool insert(const pair<K, V>& kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
_root->_col = BLACK;
return true;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (kv.first < cur->_kv.first)
{
parent = cur;
cur = cur->_left;
}
else if (kv.first > cur->_kv.first)
{
parent = cur;
cur = cur->_right;
}
else
{
return false;
}
}
cur = new Node(kv);
cur->_col = RED;
if (parent->_kv.first>kv.first)
{
parent->_left = cur;
}
else
{
parent->_right = cur;
}
cur->_parent = parent;
while (parent && parent->_col == RED)
{
Node* grandfather = parent->_parent;
if (parent == grandfather->_left)
{
Node* uncle = grandfather->_right;
if (uncle && uncle->_col == RED)
{
grandfather->_col = RED;
parent->_col = BLACK;
uncle->_col = BLACK;
cur = grandfather;
parent = cur->_parent;
}
else
{
if (cur == parent->_left)
{
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
else
{
Node* uncle = grandfather->_left;
if (uncle && uncle->_col == RED)
{
grandfather->_col = RED;
parent->_col = BLACK;
uncle->_col = BLACK;
cur = grandfather;
parent = cur->_parent;
}
else
{
if (cur == parent->_right)
{
RotateL(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
RotateR(parent);
RotateL(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
}
_root->_col = BLACK;
return true;
}
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
Node* parentParent = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
if (parentParent == nullptr)
{
_root = subL;
subL->_parent = nullptr;
}
else
{
if (parent == parentParent->_left)
{
parentParent->_left = subL;
}
else
{
parentParent->_right = subL;
}
subL->_parent = parentParent;
}
}
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
Node* parentParent = parent->_parent;
subR->_left = parent;
parent->_parent = subR;
if (parentParent == nullptr)
{
_root = subR;
subR->_parent = nullptr;
}
else
{
if (parent == parentParent->_left)
{
parentParent->_left = subR;
}
else
{
parentParent->_right = subR;
}
subR->_parent = parentParent;
}
}
void Inoder()
{
_Inoder(_root);
cout << endl;
}
void _Inoder(Node*root)
{
if (root == nullptr)
return;
_Inoder(root->_left);
cout << root->_kv.first<< " ";
_Inoder(root->_right);
}
bool Check(Node* root, int blacknum, const int refnum)
{
if (root == nullptr)
{
if (blacknum == refnum)
return true;
else
{
cout << "存在黑色节点的数量不相等的路径" << endl;
return false;
}
}
if (root->_col == RED && root->_parent && root->_parent->_col == RED)
{
cout << "存在连续的红色结点" << endl;
return false;
}
if (root->_col == BLACK)
{
blacknum++;
}
return Check(root->_left, blacknum, refnum) && Check(root->_right, blacknum, refnum);
}
bool IsBalanceTree()
{
if (_root == nullptr)
return true;
if (_root->_col == RED)
return false;
Node* cur = _root;
int refnum = 0;
while (cur)
{
if (cur->_col == BLACK)
{
++refnum;
}
cur = cur->_left;
}
return Check(_root, 0, refnum);
}
Node* Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (cur->_kv.first < key)
{
cur = cur->_right;
}
else if (cur->_kv.first > key)
{
cur = cur->_left;
}
else
{
return cur;
}
}
return nullptr;
}
private:
Node* _root = nullptr;
};