🌻个人主页:路飞雪吖~
🌠专栏:C/C++
目录
一、红黑树的概念
红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出两倍,因而使接近平衡的。
二、红黑树的性质
• 每个节点不是红色就是黑色
• 根节点是黑色
• 如果一个结点是红色,则它的两个孩子结点是黑色(即不能有两个连续的红色结点)
• 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点
• 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)
满足以上的性质,红黑树就能保证:其 最长路径 不超过 最短路径的2倍
最短路径:全黑 O(logN)
最长路径:一黑一红 O(2*logN)
三、红黑树的实现
🌟红黑树的节点定义
//结点的颜色
enum Colour
{
RED,
BLACK
};
template<class K, class V>
struct RBTreeNode
{
pair<K, V> _kv;//结点的值域
RBTreeNode<K, V>* _left;//结点的左孩子
RBTreeNode<K, V>* _right;//结点的右孩子
RBTreeNode<K, V>* _parent;//结点的双亲(红黑树需要旋转,为了实现简单给出)
Colour _col;//结点的颜色
RBTreeNode(const pair<K, V>& kv)
:_kv(kv)
, _left(nullptr)
, _right(nullptr)
, _parent(nullptr)
{}
};
🌟红黑树的结构
为了后续实现关联式容器简单,红黑树的实现中增加一个头结点,因为根节点必须为黑色,为了 与根节点进行区分,将头结点给成黑色,并且让头结点的 pParent 域指向红黑树的根节点,pLeft 域指向红黑树中最小的节点,_pRight域指向红黑树中最大的节点
🌟基本框架:
• 结点的颜色
• 结点的定义
• 实现的框架
//结点的颜色
enum Colour
{
RED,
BLACK
};
template<class K, class V>
struct RBTreeNode
{
pair<K, V> _kv;//结点的值域
RBTreeNode<K, V>* _left;//结点的左孩子
RBTreeNode<K, V>* _right;//结点的右孩子
RBTreeNode<K, V>* _parent;//结点的双亲(红黑树需要旋转,为了实现简单给出)
Colour _col;//结点的颜色
RBTreeNode(const pair<K, V>& kv)
:_kv(kv)
, _left(nullptr)
, _right(nullptr)
, _parent(nullptr)
{}
};
template<class K, class V>
class RBTree
{
typedef RBTreeNode<K, V> Node;
public:
// 具体的函数实现......
private:
Node* _root = nullptr;
};
🌟旋转
🌠左单旋
• parent的右指向subRL
• subR的左指向parent
• 判断是否为根
(具体过程可看文章的上一篇 ^ ^)
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
//subRL
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
Node* parentParent = parent->_parent;
//subR
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;
}
}
🌠右单旋
• parent的左指向subLR
• subL的右指向parent
• 判断是否为根
(具体过程可看文章的上一篇 ^ ^)
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;
}
}
🌟红黑树的插入
我们新插入的结点是黑色的还是红色的呢?
如果我们插入的是黑色结点,就会导致每条路径下黑色结点的数量不相同;
如果我们插入的是红色结点,就会导致出现连续的红色结点,意味着最长路径已经超过最短路径的2倍了,此时就需要看情况进行 变色 或者 变色+旋转 进行处理。红黑树达到的是近似平衡。
红黑树是在二叉搜索树的基础上加上其平衡限制条件,因此红黑树的插入可分为两步:
1、按照二叉搜索树的规则插入新节点
• 根结点为空,直接把新增节点作为根节点;
• 根不为空,判断新插入的节点是在根的左边(小于根)还是右边(大于根);
• 找到位置,创建节点,判断新插的节点在前一个结点的左边还是右边,并与前一个结点进行连接;
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->_right;
}
else if (cur->_kv.first > kv.first)
{
parent = cur;
cur = cur->_left;
}
else
{
//找到相同的数,返回false
return false;
}
}
//插入新节点,颜色给红色
cur = new Node(kv);
cur->_col = RED;
if (parent->_kv.first < kv.first)
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
cur->_parent = parent;//与parent链接
//插入新节点后,红黑树的性质是否造成破化......
//在这里进行检查
return true;
}
2、检查新节点插入后,红黑树的性质是否造成破坏(变色、旋转)
因为新节点的默认颜色是红色,因此:如果其双亲结点的颜色是黑色,没有违法红黑树任何性质,则不需要调整;但当新插入结点的双亲结点颜色为红色时,就违反了性质(不能有两个连续的红节点),此时就需要对红黑树分情况来讨论:
约定: cur为当前结点,p为父节点,g为祖父结点
(1) p 为 g 的 左
注:此处看到的树,有可能是一颗完整的树,也可能是一颗子树。
while (parent && parent->_col == RED)
{ //(1)
// g
// p u
Node* grandfather = parent->_parent;
if (parent == grandfather->_left)
{
Node* uncle = grandfather->_right;
//情况一:
if (uncle && uncle->_col == RED)//u存在且为红
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
//继续往上调整
cur = grandfather;
parent = cur->_parent;
}
else//情况二: u不存在 或 存在为黑
{
//...
}
}
else //parent == grandfather->_right
{
//(2)...
}
}
_root->_col = BLACK;//根始终为黑
return true;
else//情况二: u不存在 或 存在为黑
{
// g
// p u
// c
//单旋+变色
if (cur == parent->_left)
{
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
// g
// p u
// c
// 双旋+变色
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
(2) p 为 g 的 右
else //parent == grandfather->_right
{ //情况一:
// g
// u p
Node* uncle = grandfather->_left;
if (uncle && uncle->_col == RED)// u存在 且 为红
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
//继续往上调整
cur = grandfather;
parent = cur->_parent;
}
else//情况二:u不存在 或 存在为黑
{
//...
}
}
_root->_col = BLACK;//根始终为黑
return true;
else//情况二:u不存在 或 存在为黑
{
// g
// u p
// c
// 单旋+变色
if (cur == parent->_right)
{
RotateL(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
// g
// u p
// c
// 双旋+变色
RotateR(parent);
RotateL(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
🌟红黑树的验证
1、检测其是否满足二叉搜索树(中序遍历是否为有序序列)
2、检测是否满足红黑树的性质
• 根节点为黑
• 每条路径的黑色结点数量相同
• 不存在两个连续的红色结点
bool IsBalance()
{
if (_root == nullptr)
{
return true;
}
if (_root->_col == RED)
{
return false;
}
//找一条路径作为参考值
int refNum = 0;
Node* cur = _root;
while (cur)
{
if (cur->_col == BLACK)
{
++refNum;//黑色结点的数量
}
cur = cur->_left;
}
return Check(_root, 0, refNum);
}
private:
bool Check(Node* root, int blackNum, const int refNum)
{
if (root == nullptr)
{
cout << blackNum << endl;
if (refNum != blackNum)
{
cout << "存在黑色结点的数量不相等的路径" << endl;
return false;
}
return true;
}
//检查是否有连续的红节点
if (root->_col == RED && root->_parent->_col == RED)
{
cout << root->_kv.first << "存在连续的红节点" << endl;
return false;
}
if (root->_col == BLACK)
{
blackNum++;
}
return Check(root->_left, blackNum, refNum)
&& Check(root->_right, blackNum, refNum);
}
四、红黑树与AVL树的比较
红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O(log₂N),红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以红黑树在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用红黑树更多。
红黑树和AVL树的搜索谁的效率更高?单论Find而言,AVL树效率更高。
• AVL树 --- 严格平衡
• 红黑树 -- 近似平衡
五、实现的代码
#pragma once
#include<iostream>
using namespace std;
enum Colour
{
RED,
BLACK
};
template<class K, class V>
struct RBTreeNode
{
pair<K, V> _kv;
RBTreeNode<K, V>* _left;
RBTreeNode<K, V>* _right;
RBTreeNode<K, V>* _parent;
Colour _col;
RBTreeNode(const pair<K, V>& kv)
:_kv(kv)
,_left(nullptr)
,_right(nullptr)
,_parent(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);
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);
cur->_col = RED;
if (parent->_kv.first < kv.first)
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
cur->_parent = parent;
//判断新插入的结点是否破坏规则,破坏规则进行处理
while (parent && parent->_col == RED)
{
// g
// p u
Node* grandfather = parent->_parent;
if (parent == grandfather->_left)
{
Node* uncle = grandfather->_right;
//情况一:
if (uncle && uncle->_col == RED)//u存在且为红
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
cur = grandfather;
parent = cur->_parent;
}
else//情况二: u不存在 或 存在为黑
{
// g
// p u
// c
if (cur == parent->_left)
{
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
// g
// p u
// c
// 旋转+变色
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
else //parent == grandfather->_right
{
// g
// u p
Node* uncle = grandfather->_left;
if (uncle && uncle->_col == RED)// u存在 且 为红
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
cur = grandfather;
parent = cur->_parent;
}
else//u不存在 或 存在为黑
{
// g
// u p
// c
if (cur == parent->_right)
{
RotateL(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
// g
// u p
// c
RotateR(parent);
RotateL(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
}
_root->_col = BLACK;
return true;
}
Node* Find(const K& key)
{
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_kv.first < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_kv.first > key)
{
parent = cur;
cur = cur->_left;
}
else
{
return cur;
}
}
return nullptr;
}
int Height()
{
return _Height(_root);
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
int Size()
{
return _Size(_root);
}
bool IsBalance()
{
if (_root == nullptr)
{
return true;
}
if (_root->_col == RED)
{
return false;
}
//找一条路径作为参考值
int refNum = 0;
Node* cur = _root;
while (cur)
{
if (cur->_col == BLACK)
{
++refNum;//黑色结点的数量
}
cur = cur->_left;
}
return Check(_root, 0, refNum);
}
private:
bool Check(Node* root, int blackNum, const int refNum)
{
if (root == nullptr)
{
cout << blackNum << endl;
if (refNum != blackNum)
{
cout << "存在黑色结点的数量不相等的路径" << endl;
return false;
}
return true;
}
//检查是否有连续的红节点
if (root->_col == RED && root->_parent->_col == RED)
{
cout << root->_kv.first << "存在连续的红节点" << endl;
return false;
}
if (root->_col == BLACK)
{
blackNum++;
}
return Check(root->_left, blackNum, refNum)
&& Check(root->_right, blackNum, refNum);
}
int _Size(Node* root)
{
return root == nullptr ? 0 : _Size(root->_left) + _Size(root->_right) + 1;
}
void _InOrder(Node* root)
{
if (root == nullptr)
return;
_InOrder(root->_left);
cout << root->_kv.first << ":" << root->_kv.second << endl;
_InOrder(root->_right);
}
int _Height(Node* root)
{
if (root == nullptr)
return 0;
int leftHeight = _Height(root->_left);
int rightHeight = _Height(root->_right);
return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}
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 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;
}
}
private:
Node* _root = nullptr;
};
void RBTreetest1()
{
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 });
//cout << e << "->" << t.IsBalance() << endl;
}
t.InOrder();
cout << t.IsBalance() << endl;
}
如若对你有帮助,记得点赞、收藏、关注哦!
若有误,望各位,在评论区留言或者私信我 指点迷津!!!谢谢^ ^ ~