之前我们学习了AVL树,这是一种判断左右子树高度来严格控制总体树的高度的树。条件相对来说比较苛刻。我们今天要学习的红黑树,它不是通过左右子树高度来控制总体高度,而是通过结点的颜色来控制树的高度。
从字面意思上来说,红黑树,就是树的结点要么是黑的,要么是红的。
但是这个红黑树的红色结点和黑色结点不是想怎么排就怎么排,红黑结点的插入顺序是有讲究的:
- 每个结点不是红色就是黑色
- 根节点是黑色的
- 如果一个节点是红色的,则它的两个孩子结点是黑色的
- 对于每个结点,从该结点到其所有后代叶结点的简单路径上均包含相同数目的黑色结点
- 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)
其中2,3是控制树的高度的手段。我们来看第2条,第2条说白了就是:不能出现连续的红色结点。
对于第3条,我们研究两种极端情况:
对于我们来说,左边这种全部是黑色结点的路径为最短路径,右边这种一黑一红的路径我们称为最长路径。如果我们以每条路径上的黑色结点作为我们的路径长度,那么我们我们一棵树每一条的路径长度都应该在N~2N之间。这样保证了树的高度。
这里注意第5点,这里的叶子结点指的是空节点,空节点是黑色。
了解完红黑树的基本概念之后,我们来上手写一下红黑树,我们先把红黑树的结点封装好:
//红黑树的颜色因子
enum Color
{
RED,
BLACK
};
//红黑树结点封装
template <class K,class V>
struct RBTreeNode
{
RBTreeNode<K, V>* _leftchild;
RBTreeNode<K, V>* _rightchild;
RBTreeNode<K, V>* _parent;
Color _col; //颜色因子
pair<K, V> _kv;
RBTreeNode(const pair<K,V>& kv)
:_leftchild(nullptr)
,_rightchild(nullptr)
,_parent(nullptr)
,_col(RED)
,_kv(kv)
{
}
};
我们也来看看红黑树的插入:首先红黑树是搜索二叉树,插入的逻辑和之前的搜索二叉树的逻辑是一样的。
//红黑树
template <class K,class V>
class RBTree
{
typedef RBTreeNode<K, V> _Node;
private:
bool Insert(const pair<K,V>& kv)
{
if (_root == nullptr)
{
_root = new _Node(kv);
return true;
}
_Node* parent = nullptr;
_Node* cur = _root;
while (cur)
{
if (cur->_kv.first < kv.first)
{
parent = cur;
cur = cur->_rightchild;
}
else if (cur->_kv.first < kv.first)
{
parent = cur;
cur = cur->_leftchild;
}
else
{
return false;
}
}
cur = new _Node(kv);
if (cur->_kv.first < parent->_kv.first)
{
parent->_leftchild = cur;
cur->_parent = parent;
}
else if (cur->_kv.first > parent->_kv.first)
{
parent->_rightchild = cur;
cur->_parent = parent;
}
//失衡,调整
while ()
{
}
}
private:
_Node* _root = nullptr;
};
那么问题来了,我们是插入黑色结点好呢?还是红色结点好呢?
这里注意一下,如果直接插入黑色结点,可能会直接导致某一条路径上的黑色结点直接加一,直接导致整棵树失衡。所以这是一种不明智的做法。
所以明智的做法就是,我们插入红色结点,之后如果有失衡,还有回旋的余地。
这个时候我们可以完全不用管,因为我们并没有改变任何一条黑色路径上的黑色结点的数量,整棵树在整体上还是平衡的。
这个时候,违反了规则3,不能出现连续的红色结点,这个时候该怎么办呢?此时我们知道7这个结点是一定要变成黑色的:
这个时候7这条路径上的黑色结点莫名奇妙多了一个,为了使各路黑色结点均衡,我们将6这个结点变成红色:
这个时候5这条的黑色结点又会莫名其妙少一个,这个时候我们可以将5这个结点变成黑色:
这样是不是就好啦?
我们上面只是做了一个简单的分析,我们现在来认真分析一下:
我们这里把每个结点标好名字:约定:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点
当uncle结点为红色,parent结点为红色时,这时候我们这时候有连续的红色结点,这个时候我们将一层一层的变色,直到结点为根结点,根节点为黑色截止。
我们再来解释一下为什么直到根节点变色才结束:
如果此时的g为根节点:
这个时候虽然个没有变色,但是g为根节点,就代表每一条路径都加了一个黑色结点,每条路径上的黑色结点数量都是一样的。
如果此时的g不为根结点,而是一棵子树,就没法保证每一条路径上都增加一个黑色结点,所以这个时候变成红色结点是最保险的。
为了让情况变简单,我们在变色结束的时候,直接对根节点进行变黑处理:
//出现连续红色结点,失衡,调整
while (parent && parent->_col == RED)
{
//记录祖父
// g
// p u
//c
_Node* grandfather = parent->_parent;
//找叔叔结点
if (grandfather->_leftchild == parent)
{
_Node* uncle = grandfather->_rightchild;
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
//向上迭代
cur = grandfather;
parent = cur->_parent;
}
}
}
_root->_col = BLACK;
return true;
此时还有第二种情况:如果uncle结点为空,或者uncle结点为黑色。
我们先来看uncle结点为空的情况:
这个时候,我们发现单纯的变色已经解决不了问题了,这个时候,我们的想办法让这棵树平衡一点。诶?我们之前学习AVL树的时候,学习过如何让一棵树平衡,以上面的树为例,以g的视角来看,我是左子树高,所以我要进行左左旋:
这个时候高度平衡一些了,这个时候我们要处理的就是颜色问题,因为这个时候就只有颜色不平衡了,很简单,变色就是。
第一种情况我们已经讨论了,我们现在来看第二种情况,uncle结点为黑。
这个也是,树不平衡,首先进行旋转:
这个时候进变色处理:
可能这个图还是有点抽象,我们看第一棵子树,我们考虑一下:这棵树原本就是这样的吗?
如果这棵树原本就是这样的,那么,左边的黑色结点树比右边的黑色结点树要少1,肯定是不平衡的,那说明一个问题,这棵树的状态肯定还在变化之中。那么说明这棵树一开始的状态是这样的:
然后变色变上去:
这里注意一下,如果这棵树为AVL树其实从p开始就要开始调整了,因为这时候树的高度已经失衡了。但是红黑树是依据每条路径上黑色结点的数量来判定的。这时候黑色结点的数量是满足要求,但是因为不能出现连续的红色结点,所以我们以g开始旋转:
这个时候变色:
其中注意,为了保持上面的图是平衡的红黑树,所以c为带一个黑结点的红黑树,d和c要么为空,要么为一个红色结点。
了解完基本的思路之后,我们开始来写代码:
//找叔叔结点
if (grandfather->_leftchild == parent)
{
_Node* uncle = grandfather->_rightchild;
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
//向上迭代
cur = grandfather;
parent = cur->_parent;
}
else if (!uncle || uncle->_col == BLACK)
{
//旋转+变色
// g
// p
//c
if (cur == parent->_leftchild)
{
//进行左左旋
RoateLL(grandfather); //AVL树中的左左旋
parent->_col = BLACK;
grandfather->_col = RED;
}
}
那我们在AVL树中有双旋,那么在红黑树中有双旋吗?答案是有的,这就是我们的第三种情况:
第三种情况:
这种情况我们先进行一个右右旋,在进行一个左左旋就好啦!
//找叔叔结点
if (grandfather->_leftchild == parent)
{
_Node* uncle = grandfather->_rightchild;
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
//向上迭代
cur = grandfather;
parent = cur->_parent;
}
else if (!uncle || uncle->_col == BLACK)
{
//旋转+变色
// g
// p
//c
if (cur == parent->_leftchild)
{
//进行左左旋
RoateLL(grandfather); //AVL树中的左左旋
parent->_col = BLACK;
grandfather->_col = RED;
}
else if (cur == parent->_leftchild)
// g
// p
// c
{
RoateRR(parent);
RoateLL(grandfather);
cur->_col = BLACK;
grandfather = RED;
}
break; //黑色为终结态,变色完之后跳出
}
到目前为止,我们只分析了parent为grandfather的左孩子的情况,现在我们来讨论一下parent为grandfather右孩子的情况。
我们来看具体的图片:
这个我们对照着uncle为右孩子的方式来看,上图的话就单纯的变色就好了:
当uncle结点为空或者为黑的时候。
else if (grandfather->_rightchild == parent)
{
// g
// u p
// c
_Node* uncle = grandfather->_leftchild;
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = RED;
grandfather->_col = BLACK;
cur = grandfather;
parent = cur ->_parent;
}
else if (!uncle || uncle->_col == BLACK)
{
// g
// p
// c
if (cur == parent->_rightchild)
{
//进行右右旋
RoateRR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
// g // g
// p // c
// c // p
else if (cur == parent->_leftchild)
{
//进行左左旋
RoateLL(parent);
//进行右右旋
RoateRR(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
附上完整的插入代码:
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 (cur->_kv.first < kv.first)
{
parent = cur;
cur = cur->_rightchild;
}
else if (cur->_kv.first > kv.first)
{
parent = cur;
cur = cur->_leftchild;
}
else
{
return false;
}
}
cur = new _Node(kv);
if (cur->_kv.first < parent->_kv.first)
{
parent->_leftchild = cur;
cur->_parent = parent;
}
else if (cur->_kv.first > parent->_kv.first)
{
parent->_rightchild = cur;
cur->_parent = parent;
}
//出现连续红色结点,失衡,调整
while (parent && parent->_col == RED)
{
//记录祖父
// g
// p u
//c
_Node* grandfather = parent->_parent;
//找叔叔结点
if (grandfather->_leftchild == parent)
{
_Node* uncle = grandfather->_rightchild;
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
//向上迭代
cur = grandfather;
parent = cur->_parent;
}
else if (!uncle || uncle->_col == BLACK)
{
//旋转+变色
// g
// p
//c
if (cur == parent->_leftchild)
{
//进行左左旋
RoateLL(grandfather); //AVL树中的左左旋
parent->_col = BLACK;
grandfather->_col = RED;
}
else if (cur == parent->_rightchild)
// g
// p
// c
{
RoateRR(parent);
RoateLL(grandfather);
cur->_col = BLACK;
grandfather ->_col= RED;
}
break; //黑色为终结态,变色完之后跳出
}
}
else if (grandfather->_rightchild == parent)
{
// g
// u p
// c
_Node* uncle = grandfather->_leftchild;
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = RED;
grandfather->_col = BLACK;
cur = grandfather;
parent = cur ->_parent;
}
else if (!uncle || uncle->_col == BLACK)
{
// g
// p
// c
if (cur == parent->_rightchild)
{
//进行右右旋
RoateRR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
// g // g
// p // c
// c // p
else if (cur == parent->_leftchild)
{
//进行左左旋
RoateLL(parent);
//进行右右旋
RoateRR(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
}
_root->_col = BLACK;
return true;
}
void RoateLL(_Node* parent) //左左旋
// p
// subL
//cur
{
_Node* subL = parent->_leftchild;
_Node* subLR = subL->_rightchild;
//改变指向
subL->_rightchild = parent;
parent->_leftchild = subLR;
if (subLR)
subLR->_parent = parent;
//记录爷爷结点
_Node* grandfather = parent->_parent;
parent->_parent = subL;
if (_root == parent)
{
_root = subL;
subL->_parent = nullptr;
}
else
{
if (grandfather->_leftchild == parent)
{
grandfather->_leftchild = subL;
}
else if (grandfather->_rightchild == parent)
{
grandfather->_rightchild = subL;
}
subL->_parent = grandfather;
}
}
void RoateRR(_Node* parent) //右右旋
{
_Node* subR = parent->_rightchild;
_Node* subRL = subR->_leftchild;
//改变指向
subR->_leftchild = parent;
parent->_rightchild = subRL;
if (subRL)
subRL->_parent = parent;
//记录祖父
_Node* grandfather = parent->_parent;
parent->_parent = subR;
if (_root == parent)
{
_root = subR;
subR->_parent = nullptr;
}
else
{
if (grandfather->_leftchild == parent)
{
grandfather->_leftchild = subR;
}
else if (grandfather->_rightchild == parent)
{
grandfather->_rightchild = subR;
}
subR->_parent = grandfather;
}
}
我们可以用大量的数据简单测试一下:
int main()
{
const int N = 10000;
vector<int> v;
v.reserve(N);
srand(time(0));
for (size_t i = 0; i < N; i++)
{
v.push_back(rand());
//cout << v.back() << endl;
}
RBTree<int, int> t;
for (auto e : v)
{
t.Insert(make_pair(e, e));
//cout << "Insert:" << e << "->" << t.IsBalance() << endl;
}
t.InOrder();
}
但是这样只能保证这棵树是二叉搜索树,但如何证明这棵树是一棵平衡的红黑树呢?我么可以统计一条路径上的黑色结点数量,然后对比每一条路径的黑色结点是否相等。
bool Check(_Node* node,int blacknum,const int refVal)
{
if (node == nullptr)
{
if (refVal != blacknum)
{
return false;
}
cout << "黑色结点的数量:" << blacknum << endl;
return true;
}
//检查是否有连续的红色结点
if (node->_col == RED)
{
if (node->_parent->_col == RED)
{
cout << "有连续的红色结点" << endl;
return false;
}
}
//黑色结点
if (node->_col == BLACK)
{
++blacknum;
}
return Check(node->_leftchild, blacknum, refVal) &&
Check(node->_rightchild, blacknum, refVal);
}
bool isTrue()
{
if (_root == nullptr)
return true;
if (_root->_col == RED)
return false;
记录容器
//vector v;
//Check(_root,0,v);
//int first = v[0];
//int total = 0;
//for (auto e : v)
//{
// total += e;
//}
//if (total != first * v.size())
//{
// cout << "此树不平衡" << endl;
// return false;
//}
//else
//{
// return true;
//}
int blacknum = 0; //先统计一条路径上的黑色结点数量
int refVal = 0;
_Node* cur = _root;
while (cur)
{
if (cur->_col == BLACK)
refVal++;
cur = cur->_leftchild;
}
return Check(_root, blacknum,refVal);
}
#pragma once
#include
#include
using namespace std;
//红黑树的颜色因子
enum Color
{
RED,
BLACK
};
//红黑树结点封装
template <class K,class V>
struct RBTreeNode
{
RBTreeNode<K, V>* _leftchild;
RBTreeNode<K, V>* _rightchild;
RBTreeNode<K, V>* _parent;
Color _col; //颜色因子
pair<K, V> _kv;
RBTreeNode(const pair<K,V>& kv)
:_leftchild(nullptr)
,_rightchild(nullptr)
,_parent(nullptr)
,_col(RED)
,_kv(kv)
{
}
};
//红黑树
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 (cur->_kv.first < kv.first)
{
parent = cur;
cur = cur->_rightchild;
}
else if (cur->_kv.first > kv.first)
{
parent = cur;
cur = cur->_leftchild;
}
else
{
return false;
}
}
cur = new _Node(kv);
if (cur->_kv.first < parent->_kv.first)
{
parent->_leftchild = cur;
cur->_parent = parent;
}
else if (cur->_kv.first > parent->_kv.first)
{
parent->_rightchild = cur;
cur->_parent = parent;
}
//if (cur == parent->_leftchild)
//{
// parent->_leftchild = cur;
// cur->_parent = parent;
//}
//else if (cur == parent->_rightchild)
//{
// parent->_rightchild = cur;
// cur->_parent = parent;
//}
//出现连续红色结点,失衡,调整
while (parent && parent->_col == RED)
{
//记录祖父
// g
// p u
//c
_Node* grandfather = parent->_parent;
//找叔叔结点
if (grandfather->_leftchild == parent)
{
_Node* uncle = grandfather->_rightchild;
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
//向上迭代
cur = grandfather;
parent = cur->_parent;
}
else if (!uncle || uncle->_col == BLACK)
{
//旋转+变色
// g
// p
//c
if (cur == parent->_leftchild)
{
//进行左左旋
RoateLL(grandfather); //AVL树中的左左旋
parent->_col = BLACK;
grandfather->_col = RED;
}
else if (cur == parent->_rightchild)
// g
// p
// c
{
RoateRR(parent);
RoateLL(grandfather);
cur->_col = BLACK;
grandfather ->_col= RED;
}
break; //黑色为终结态,变色完之后跳出
}
}
else if (grandfather->_rightchild == parent)
{
// g
// u p
// c
_Node* uncle = grandfather->_leftchild;
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = RED;
grandfather->_col = BLACK;
cur = grandfather;
parent = cur ->_parent;
}
else if (!uncle || uncle->_col == BLACK)
{
// g
// p
// c
if (cur == parent->_rightchild)
{
//进行右右旋
RoateRR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
// g // g
// p // c
// c // p
else if (cur == parent->_leftchild)
{
//进行左左旋
RoateLL(parent);
//进行右右旋
RoateRR(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
}
_root->_col = BLACK;
return true;
}
void RoateLL(_Node* parent) //左左旋
// p
// subL
//cur
{
_Node* subL = parent->_leftchild;
_Node* subLR = subL->_rightchild;
//改变指向
subL->_rightchild = parent;
parent->_leftchild = subLR;
if (subLR)
subLR->_parent = parent;
//记录爷爷结点
_Node* grandfather = parent->_parent;
parent->_parent = subL;
if (_root == parent)
{
_root = subL;
subL->_parent = nullptr;
}
else
{
if (grandfather->_leftchild == parent)
{
grandfather->_leftchild = subL;
}
else if (grandfather->_rightchild == parent)
{
grandfather->_rightchild = subL;
}
subL->_parent = grandfather;
}
}
void RoateRR(_Node* parent) //右右旋
{
_Node* subR = parent->_rightchild;
_Node* subRL = subR->_leftchild;
//改变指向
subR->_leftchild = parent;
parent->_rightchild = subRL;
if (subRL)
subRL->_parent = parent;
//记录祖父
_Node* grandfather = parent->_parent;
parent->_parent = subR;
if (_root == parent)
{
_root = subR;
subR->_parent = nullptr;
}
else
{
if (grandfather->_leftchild == parent)
{
grandfather->_leftchild = subR;
}
else if (grandfather->_rightchild == parent)
{
grandfather->_rightchild = subR;
}
subR->_parent = grandfather;
}
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
void _InOrder(_Node* root)
{
if (root == nullptr)
return;
_InOrder(root->_leftchild);
cout << root->_kv.first << endl;
_InOrder(root->_rightchild);
}
bool Check(_Node* node, int blacknum, const int refVal)
{
if (node == nullptr)
{
if (refVal != blacknum)
{
return false;
}
cout << "黑色结点的数量:" << blacknum << endl;
return true;
}
//检查是否有连续的红色结点
if (node->_col == RED)
{
if (node->_parent->_col == RED)
{
cout << "有连续的红色结点" << endl;
return false;
}
}
//黑色结点
if (node->_col == BLACK)
{
++blacknum;
}
return Check(node->_leftchild, blacknum, refVal) &&
Check(node->_rightchild, blacknum, refVal);
}
bool isTrue()
{
if (_root == nullptr)
return true;
if (_root->_col == RED)
return false;
记录容器
//vector v;
//Check(_root,0,v);
//int first = v[0];
//int total = 0;
//for (auto e : v)
//{
// total += e;
//}
//if (total != first * v.size())
//{
// cout << "此树不平衡" << endl;
// return false;
//}
//else
//{
// return true;
//}
int blacknum = 0;
int refVal = 0;
_Node* cur = _root;
while (cur)
{
if (cur->_col == BLACK)
refVal++;
cur = cur->_leftchild;
}
return Check(_root, blacknum, refVal);
}
private:
_Node* _root = nullptr;
};
#define _CRT_SECURE_NO_WARNINGS 1
#include"RBTree.h"
int main()
{
const int N = 10000;
vector<int> v;
v.reserve(N);
srand(time(0));
for (size_t i = 0; i < N; i++)
{
v.push_back(rand());
//cout << v.back() << endl;
}
RBTree<int, int> t;
for (auto e : v)
{
t.Insert(make_pair(e, e));
//cout << "Insert:" << e << "->" << t.IsBalance() << endl;
}
t.InOrder();
t.isTrue();
}