数据结构之红黑树(RBTree)
文章目录
一、红黑树的概念
红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black,通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的
二、红黑树的性质
- 每个结点不是红色就是黑色
- 根节点是黑色的
- 如果一个节点是红色的,则它的两个孩子结点是黑色的
- 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点
- 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)
思考:为什么满足上面的性质,红黑树就能保证:其最长路径中节点个数不会超过最短路径节点个数的两倍?
三、红黑树节点的定义
enum Color
{
Red,
Black
};
template <class K,class V>
struct TreeNode
{
TreeNode(const pair<K, V>& kv)
:_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_kv(kv)
,_col(Red)
{}
TreeNode* _left;
TreeNode* _right;
TreeNode* _parent;
pair<K, V> _kv;
Color _col;
};
思考:在节点的定义中,为什么要将节点的默认颜色给成红色的?
四、红黑树结构
为了后续实现关联式容器简单,红黑树的实现中增加一个头结点,因为跟节点必须为黑色,为了与根节点进行区分,将头结点给成黑色,并且让头结点的 pParent 域指向红黑树的根节点,pLeft域指向红黑树中最小的节点,_pRight域指向红黑树中最大的节点,如下:
五、红黑树的插入操作
红黑树是在二叉搜索树的基础上加上其平衡限制条件,因此红黑树的插入可分为两步:
1. 按照二叉搜索的树规则插入新节点
先按照搜索二叉树的规则找到插入节点的位置
bool Insert(const pair<K, V>& kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
_root->_col = Black;
return true;
}
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (cur->_kv.first > kv.first)
{
parent = cur;
cur = cur->_left;
}
else if(cur->_kv.first < kv.first)
{
parent = cur;
cur = cur->_right;
}
else
{
return false;
}
}
if (parent->_kv.first > kv.first)
{
cur = new Node(kv);
parent->_left = cur;
cur->_parent = parent;
}
else
{
cur = new Node(kv);
parent->_right = cur;
cur->_parent = parent;
}
*******************************************************************
}
2. 检测新节点插入后,红黑树的性质是否造到破坏
新增:插入红色节点
1、插入节点的父亲是黑色,那么就结束了。没有违反任何规则。
2、插入节点的父亲是红色的,那么存在连续的红色节点,违反规则3,需要处理
关键看叔叔uncle其他几个节点c p g三个颜色固定
约定:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点
因为新节点的默认颜色是红色,因此:如果其双亲节点的颜色是黑色,没有违反红黑树任何性质,则不需要调整;但当新插入节点的双亲节点颜色为红色时,就违反了性质三不能有连在一起的红色节点,此时需要对红黑树分情况来讨论:
这里大分类有两类(逻辑分类):
- uncle存在且为红色(不需要判断位置关系)
- uncle存在且为黑色或者uncle不存在(需要判断位置关系,继续分类)
但实际转化为代码时,需要看cur p g u三者之间的位置关系来分类,因为不同位置调整的方式不同
- p在g的左边,u在g的右边 或者 p在g的右边,u在g的左边
- p如果在左边,cur也在左边 或者 p在右边,cur也在右边
- p如果在左边,cur在右边 或者 p在右边,cur在左边
在这三种位置关系的情况下,再结合逻辑分类进行分类
以下是分类逻辑图
情况一
这种情况下 uncle存在且为红色
情况二
分为两个小情况
-
cur在这棵树的外侧
-
cur在这棵树的内侧
3. 插入代码
bool Insert(const pair<K, V>& kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
_root->_col = Black;
return true;
}
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (cur->_kv.first > kv.first)
{
parent = cur;
cur = cur->_left;
}
else if(cur->_kv.first < kv.first)
{
parent = cur;
cur = cur->_right;
}
else
{
return false;
}
}
if (parent->_kv.first > kv.first)
{
cur = new Node(kv);
parent->_left = cur;
cur->_parent = parent;
}
else
{
cur = new Node(kv);
parent->_right = cur;
cur->_parent = parent;
}
while (parent && parent->_col == Red)
{
if (parent->_col == Black)
{
break;
}
//parent是红色,需要先找祖父,再寻找uncle
Node* grandparent = parent->_parent;
Node* uncle = nullptr;
//parent在左 uncle在右
if (grandparent->_left == parent)
{
uncle = grandparent->_right;
//uncle存在且为红色
if (uncle && uncle->_col == Red)
{
parent->_col = Black;
uncle->_col = Black;
grandparent->_col = Red;
//继续向上更新
cur = grandparent;
parent = cur->_parent;
}
//uncle存在且为黑色或者uncle不存在
else
{
//cur在左,右单旋
if (cur == parent->_left)
{
RotateR(grandparent);
parent->_col = Black;
grandparent->_col = Red;
break;
}
//cur在右,双旋
else
{
RotateL(parent);
RotateR(grandparent);
cur->_col = Black;
grandparent->_col = Red;
break;
}
}
}
//parent在右 uncle在左
else
{
uncle = grandparent->_left;
//uncle存在且为红色
if (uncle && uncle->_col == Red)
{
parent->_col = uncle->_col = Black;
grandparent->_col = Red;
cur = grandparent;
parent = cur->_parent;
}
//uncle存在且为黑色或uncle不存在
else
{
//cur在右,左单旋
if (cur == parent->_right)
{
RotateL(grandparent);
parent->_col = Black;
grandparent->_col = Red;
break;
}
//cur在左,双旋
else
{
RotateR(parent);
RotateL(grandparent);
cur->_col = Black;
grandparent->_col = Red;
break;
}
}
}
}
_root->_col = Black;
return true;
}
六、红黑树的验证
根据红黑树的规则,我们可以中序遍历这棵树打印值来检验是否符合规则,但仅仅这样是不能完全检测出的,我们需要根据红黑树的规则,来设计函数来检测这棵树
- 每个结点不是红色就是黑色
- 根节点是黑色的
- 如果一个节点是红色的,则它的两个孩子结点是黑色的
- 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点
- 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)
规则1与2只需再开始时检测一边
规则3如果之间判断会不好判断,但我们可以转换思路,如果该节点是红色的,那么他的父节点一定是黑色的,如果是红色就是违背了规则
规则4需要在每条路径走到空的时候与参考值比较,不相等就违背规则,参考值是在调用之前算出某条路径的黑色节点数
bool Isbalance()
{
if (_root == nullptr)
{
return true;
}
if (_root->_col == Red)
{
return false;
}
size_t reference = 0;
Node* cur = _root;
while (cur)
{
if (cur->_col == Black)
{
reference++;
}
cur = cur->_left;
}
return _Isbalance(_root, reference, 0);
}
bool _Isbalance(Node* root, size_t reference, size_t bnum)
{
if (root == nullptr)
{
if (bnum != reference)
{
cout << "每条路径黑色节点个数不同" << endl;
return false;
}
return true;
}
if (root->_col == Red)
{
if (root->_parent->_col != Black)
{
cout << "出现连续的红色节点" << endl;
return false;
}
}
else
{
bnum++;
}
return _Isbalance(root->_left, reference, bnum) && _Isbalance(root->_right, reference, bnum);
}
七、整体代码(含测试)
RBTree.h
#include <iostream>
using namespace std;
enum Color
{
Red,
Black
};
template <class K,class V>
struct TreeNode
{
TreeNode(const pair<K, V>& kv)
:_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_kv(kv)
,_col(Red)
{}
TreeNode* _left;
TreeNode* _right;
TreeNode* _parent;
pair<K, V> _kv;
Color _col;
};
template <class K, class V>
class RBTree
{
typedef TreeNode<K, V> Node;
public:
bool Insert(const pair<K, V>& kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
_root->_col = Black;
return true;
}
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (cur->_kv.first > kv.first)
{
parent = cur;
cur = cur->_left;
}
else if(cur->_kv.first < kv.first)
{
parent = cur;
cur = cur->_right;
}
else
{
return false;
}
}
if (parent->_kv.first > kv.first)
{
cur = new Node(kv);
parent->_left = cur;
cur->_parent = parent;
}
else
{
cur = new Node(kv);
parent->_right = cur;
cur->_parent = parent;
}
while (parent && parent->_col == Red)
{
if (parent->_col == Black)
{
break;
}
//parent是红色,需要先找祖父,再寻找uncle
Node* grandparent = parent->_parent;
Node* uncle = nullptr;
//parent在左 uncle在右
if (grandparent->_left == parent)
{
uncle = grandparent->_right;
//uncle存在且为红色
if (uncle && uncle->_col == Red)
{
parent->_col = Black;
uncle->_col = Black;
grandparent->_col = Red;
//继续向上更新
cur = grandparent;
parent = cur->_parent;
}
//uncle存在且为黑色或者uncle不存在
else
{
//cur在左,右单旋
if (cur == parent->_left)
{
RotateR(grandparent);
parent->_col = Black;
grandparent->_col = Red;
break;
}
//cur在右,双旋
else
{
RotateL(parent);
RotateR(grandparent);
cur->_col = Black;
grandparent->_col = Red;
break;
}
}
}
//parent在右 uncle在左
else
{
uncle = grandparent->_left;
//uncle存在且为红色
if (uncle && uncle->_col == Red)
{
parent->_col = uncle->_col = Black;
grandparent->_col = Red;
cur = grandparent;
parent = cur->_parent;
}
//uncle存在且为黑色或uncle不存在
else
{
//cur在右,左单旋
if (cur == parent->_right)
{
RotateL(grandparent);
parent->_col = Black;
grandparent->_col = Red;
break;
}
//cur在左,双旋
else
{
RotateR(parent);
RotateL(grandparent);
cur->_col = Black;
grandparent->_col = Red;
break;
}
}
}
}
_root->_col = Black;
return true;
}
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
subR->_left = parent;
Node* parentparent = parent->_parent;
if (subRL)
subRL->_parent = parent;
parent->_parent = subR;
if (parent == _root)
{
subR->_parent = nullptr;
_root = subR;
}
else
{
subR->_parent = parentparent;
if (parentparent->_left == parent)
{
parentparent->_left = subR;
}
else
{
parentparent->_right = subR;
}
}
}
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
subL->_right = parent;
Node* parentparent = parent->_parent;
if (subLR)
{
subLR->_parent = parent;
}
parent->_parent = subL;
if (parent == _root)
{
subL->_parent = nullptr;
_root = subL;
}
else
{
subL->_parent = parentparent;
if (parentparent->_left == parent)
{
parentparent->_left = subL;
}
else
{
parentparent->_right = subL;
}
}
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
bool Isbalance()
{
if (_root == nullptr)
{
return true;
}
if (_root->_col == Red)
{
return false;
}
size_t reference = 0;
Node* cur = _root;
while (cur)
{
if (cur->_col == Black)
{
reference++;
}
cur = cur->_left;
}
return _Isbalance(_root, reference, 0);
}
private:
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_kv.first << " ";
_InOrder(root->_right);
}
bool _Isbalance(Node* root, size_t reference, size_t bnum)
{
if (root == nullptr)
{
if (bnum != reference)
{
cout << "每条路径黑色节点个数不同" << endl;
return false;
}
return true;
}
if (root->_col == Red)
{
if (root->_parent->_col != Black)
{
cout << "出现连续的红色节点" << endl;
return false;
}
}
else
{
bnum++;
}
return _Isbalance(root->_left, reference, bnum) && _Isbalance(root->_right, reference, bnum);
}
Node* _root = nullptr;
};
RBTree.c
#include "RBTree.h"
#define N 1000
int main()
{
srand((unsigned int)time(NULL));
RBTree<int, int> t;
for (int i = 0; i < N; i++)
{
int sum = rand();
cout << "插入数据为" << sum;
t.Insert(make_pair(sum, sum));
if (t.Isbalance())
{
cout << "插入后符合规则" << endl;
}
else
{
cout << "插入后不符合规则" << endl;
}
}
return 0;
}