C++红黑树RBTree的插入
文章目录
1.引言
面对”社会大哥“红黑树,相比很多没学过红黑树的小伙伴也听说过它的大名。
红黑树是一种特殊的平衡搜索二叉树,它的平衡策略跟AVLTree不一样,AVLTree是绝对平衡,而RBTree是近似平衡。因此从搜索效率上来说没有AVLTree好,但是由于是近似平衡,需要旋转的次数也变少了,它的插入性能比AVLTree好很多。正是因为它旋转条件不那么严格的设定,所以在现实中,RBTree的使用场景和使用次数远高于AVLTree,map和set底层即用RBTree红黑树来进行封装的。
2.介绍
概念:红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或者black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的.
对此,它的性质应当满足以下几点:
1.每个节点颜色不是黑色就是红色
2.根节点颜色是黑色的
3.当一个节点颜色为红色时,其子节点颜色必定为黑色
4.对于每个节点,从该节点到其所有后代的简单路径上,均包含相同数目的黑色节点
5.每个叶子节点都是指空节点且均为黑色
为什么以上几点红黑树就能保证最长路径节点个数不会超过最短路径的两倍?
首先我们来设想一下最坏情况(假设整个树的节点树在[N,2N]之间):
最长路径:应该为红黑相间的节点路径,此时最长路径为:2logN
最短路径:应该为全部都为黑节点路径,此时最短路径为:logN
这样一来,每条路径的长度就会被严格控制在**[logN,2logN]**之间。当然就不会超过最短路径的两倍。
3.具体分析
3.1走完搜索树
红黑树本质上还是一种搜索树,所以在写红黑树的插入函数Insert()之前,肯定要先走完搜索树的部分
bool Insert(const pair<K,V>& kv)
{
if (_root == nullptr) //首先判断为空情况,根节点为空就是第一次插入
{ //第一次插入直接new一个节点给_root
_root = new Node(kv);
_root->_col = BLACK;
return true;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur) //cur不走到底就继续
{
if (cur->_kv.first < kv.first) //大了往右走
{
parent = cur;
cur = cur->_right;
}
else if (cur->_kv.first > kv.first) //小了往左走
{
parent = cur;
cur = cur->_left;
}
else //值相等的情况搜索树不允许,直接驳回
{
return false;
}
}
cur = new Node(kv); //new一个新节点,直接链接
//需要判断cur是在parent节点的左边还是右边
if (parent->_kv.first > kv.first)
{
parent->_left = cur;
}
else
{
parent->_right = cur;
}
cur->_parent = parent;
}
搜索树走完后开始走红黑树专有的部分了。
由于新插入的是红色节点,所以如果插入后,前面的节点是黑色节点,是红黑相间,那么就不用调整。
如果新插入的红色节点后,前面的节点是红色节点,那么存在两个连续的红色节点了,就需要做出调整,那么怎么调整呢?
红黑树的调整关键看叔叔uncle节点!!
有如下三种情况:(u代表uncle节点,g代表grandfather节点,p代表parent节点,c代表cur当前节点)
3.2 uncle存在且为红
如上图: 解决方案是: 1. parent变黑,uncle变黑 ,grandfather变红 2.继续向上调整
3.3uncle不存在/uncle存在且为黑(单旋)
如上图: 解决方案是:1.单旋(左单旋或者右单旋) 2.grandfather变红,parent变黑
3.4uncle不存在/uncle存在且为黑(双旋)
如上图: 解决方案是:1.parent单旋 2.grandfather单旋 3.cur变黑,grandfather变红
4.具体实现
4.1枚举颜色类型
这里采用枚举类型来渲染节点的颜色
enum Colour
{
BLACK,
RED,
};
4.2红黑树节点定义RBTreeNode
每个节点应该具有左指针、右指针、双亲指针、kv对存储数据和颜色
template<class K, class V>
struct RBTreeNode
{
RBTreeNode<K, V>* _left;
RBTreeNode<K, V>* _right;
RBTreeNode<K, V>* _parent;
pair<K, V> _kv;
Colour _col;
RBTreeNode(const pair<K,V>& kv) //构造函数
:_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_kv(kv)
,_col(RED)
{}
};
4.3 RBTree类结构&&Insert()函数
bool Insert(const pair<K,V>& kv)
{
//......
//走完前面的搜索树流程
//搜索树走完,开始走红黑树特有部分
while (parent && parent->_col == RED)
{
Node* grandfather = parent->_parent;
if (grandfather->_left == parent) //u在右边
{
Node* uncle = grandfather->_right;
if (uncle && uncle->_col == RED)
{
parent->_col = BLACK;
uncle->_col = BLACK;
grandfather->_col = RED;
cur = parent;
parent = cur->_parent;
}
else //情况2+3
{
if (parent->_left == cur)
{
// g
// p u
// c
RotateR(grandfather);
grandfather->_col = RED;
parent->_col = BLACK;
}
else
{
// g
// p u
// c
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
else //u在左边
{
Node* uncle = grandfather->_left;
if (uncle && uncle->_col == RED)
{
parent->_col = BLACK;
uncle->_col = BLACK;
grandfather->_col = RED;
cur = grandfather;
parent = cur->_parent;
}
else
{
if (cur == parent->_left)
{
// g
// u p
// c
RotateR(parent);
RotateL(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
else
{
// g
// u p
// c
RotateL(grandfather);
grandfather->_col = RED;
parent->_col = BLACK;
}
break;
}
}
}
}
4.4 红黑树的验证
红黑树的特征是按照上面五条规则来约束的,那么我们红黑树的验证也将围绕这几条来验证
bool Isbalance()
{
if (_root && _root->_col == RED)
{
cout << "根节点为红色" << endl;
return false;
}
int benchmark = 0;
Node* cur = _root;
while (cur)
{
if (cur->_col == BLACK)
{
++benchmark;
}
cur = cur->_left;
}
return _Check(_root, 0,benchmark);
}
int _Height(Node* root) //高度函数
{
if (root == nullptr)
return 0;
int leftH = _Height(root->_left);
int rightH = _Height(root->_right);
return leftH > rightH ? leftH + 1 : rightH + 1;
}
//检查是否是红黑树函数
bool _Check(Node* root, int blackNum, int benchmark)
{
if (root == nullptr)
{
if (blackNum != benchmark)
{
cout << "某条路径黑色节点不一致" << endl;
return false;
}
return true;
}
if (root->_col == BLACK)
blackNum++;
if (root->_col == RED
&& root->_parent
&& root->_parent->_col == RED)
{
cout << "存在连续红节点" << endl;
return false;
}
return _Check(root->_left, blackNum, benchmark)
&& _Check(root->_right, blackNum, benchmark);
}
4.5 红黑树的结果
这样说明一棵完整的红黑树就构建好了
4.6 全部代码
#pragma once
namespace Arthur
{
enum Colour
{
BLACK,
RED,
};
template<class K, class V>
struct RBTreeNode
{
RBTreeNode<K, V>* _left;
RBTreeNode<K, V>* _right;
RBTreeNode<K, V>* _parent;
pair<K, V> _kv;
Colour _col;
RBTreeNode(const pair<K,V>& kv)
:_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_kv(kv)
,_col(RED)
{}
};
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->_right;
}
else if (cur->_kv.first > kv.first)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
cur = new Node(kv); //链接
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 (grandfather->_left == parent) //u在右边
{
Node* uncle = grandfather->_right;
if (uncle && uncle->_col == RED)
{
parent->_col = BLACK;
uncle->_col = BLACK;
grandfather->_col = RED;
cur = parent;
parent = cur->_parent;
}
else //情况2+3
{
if (parent->_left == cur)
{
// g
// p u
// c
RotateR(grandfather);
grandfather->_col = RED;
parent->_col = BLACK;
}
else
{
// g
// p u
// c
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
else //u在左边
{
Node* uncle = grandfather->_left;
if (uncle && uncle->_col == RED)
{
parent->_col = BLACK;
uncle->_col = BLACK;
grandfather->_col = RED;
cur = grandfather;
parent = cur->_parent;
}
else
{
if (cur == parent->_left)
{
// g
// u p
// c
RotateR(parent);
RotateL(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
else
{
// g
// u p
// c
RotateL(grandfather);
grandfather->_col = RED;
parent->_col = BLACK;
}
break;
}
}
}
_root->_col = BLACK;
return true;
}
void Inorder()
{
_Inorder(_root);
}
int Height()
{
return _Height(_root);
}
bool Isbalance()
{
if (_root && _root->_col == RED)
{
cout << "根节点为红色" << endl;
return false;
}
int benchmark = 0;
Node* cur = _root;
while (cur)
{
if (cur->_col == BLACK)
{
++benchmark;
}
cur = cur->_left;
}
return _Check(_root, 0,benchmark);
}
private:
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
Node* pparent = parent->_parent;
subR->_left = parent;
parent->_parent = subR;
if (pparent == nullptr)
{
_root = subR;
subR->_parent = nullptr;
}
else
{
if (pparent->_left == parent)
{
pparent->_left = subR;
}
else
{
pparent->_right = subR;
}
subR->_parent = pparent;
}
}
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
Node* pparent = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
if (pparent == nullptr)
{
_root = subL;
_root->_parent = nullptr;
}
else
{
if (pparent->_left == parent)
{
pparent->_left = subL;
}
else
{
pparent->_right = subL;
}
subL->_parent = pparent;
}
}
void RotateLR(Node* parent)
{
RotateL(parent->_left);
RotateR(parent);
}
void RotateRL(Node* parent)
{
RotateR(parent->_right);
RotateL(parent);
}
void _Inorder(Node* root)
{
if (root == nullptr)
return;
_Inorder(root->_left);
cout << root->_kv.first << " ";
_Inorder(root->_right);
}
int _Height(Node* root)
{
if (root == nullptr)
return 0;
int leftH = _Height(root->_left);
int rightH = _Height(root->_right);
return leftH > rightH ? leftH + 1 : rightH + 1;
}
//检查是否是红黑树函数
bool _Check(Node* root, int blackNum, int benchmark)
{
if (root == nullptr)
{
if (blackNum != benchmark)
{
cout << "某条路径黑色节点不一致" << endl;
return false;
}
return true;
}
if (root->_col == BLACK)
blackNum++;
if (root->_col == RED
&& root->_parent
&& root->_parent->_col == RED)
{
cout << "存在连续红节点" << endl;
return false;
}
return _Check(root->_left, blackNum, benchmark)
&& _Check(root->_right, blackNum, benchmark);
}
private:
Node* _root = nullptr;
};
void test_RBTree()
{
RBTree<int, int> sb;
sb.Insert(make_pair(1, 1));
sb.Insert(make_pair(3, 3));
sb.Insert(make_pair(2, 2));
sb.Insert(make_pair(4, 4));
sb.Inorder();
cout << endl;
cout<<sb.Height();
cout << endl;
cout << sb.Isbalance();
}
}
5.尾言
本着学习思路的原则,所以这里只介绍了红黑树的插入,红黑树的删除的基本思路跟插入大差不差,所以没必要再次引入,因为精华部分就是红黑树的插入罢了,咱们学知识只学精华就ok了!
本篇到此结束,如果你喜欢我的内容,麻烦手动点个赞吧!
还想要关注我更多内容,请看: