红黑树
一. 概念
红黑树本质上也是一种二叉搜索树, 只不过在每个节点上增加了一个存储位表示当前节点的颜色, 可以是RED或者BLACK, 通过对每个节点颜色的限制, 使得红黑树是一棵接近平衡的树.
例如 :
二. 性质
- 每个节点的颜色只有黑色和红色两种
- 根节点一定是黑色的
- 红色节点不能相连, 即如果一个节点是红色, 它的左右孩子一定是黑色
- 对于任何一个节点, 从该节点到所有叶子节点的路径上黑色的节点数目相同
- 空节点都是黑色的
三. 红黑树的结构
红黑树的节点定义为
enum Color
{
Red,
Black
};
template <class K,class V>
struct RBTNode
{
RBTNode(const pair<K,V>& data = pair<K,V>())
: _left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _data(data)
, _color(Red)
{}
RBTNode<K,V>* _left;
RBTNode<K,V>* _right;
RBTNode<K,V>* _parent;
pair<K,V> _data;
Color _color;
};
为了后序实现关联式容器Map和Set, 红黑树的实现中增加一个头结点, 头结点的 _parent 域指向红黑树的根节点,_left域指向红黑树中最小的节点,_right域指向红黑树中最大的节点
****这里我们有一个问题****
在节点的定义中,为什么要将节点的默认颜色给成红色的?
答 : 如果是黑的话, 插入进去一定会破坏每条路径上的黑色的节点数相等这个条件, 但是红色不一定会破坏相应的条件, 后面调整的话红色也方便一些.
四. 操作(主要)
1. 左旋 (与上一篇文章AVL树的旋转基本相同)
2. 右旋(同上)
3. 插入
红黑树是在二叉搜索树的基础上加上其平衡限制条件,因此红黑树的插入可分为两步:
- 按照二叉搜索的树规则插入新节点
- 检测新节点插入后,红黑树的性质是否造到破坏, 如果破坏, 要进行调整
调整分为下面几种 :
- 情况一 : 父亲为红, 祖父为黑, 叔叔存在且为红 ,直接插入, 修改父亲和祖父的颜色, 即
- 情况二 : 父亲为红, 祖父为黑, 叔叔不存在/叔叔为黑, 父亲节点在祖父节点的左边
可以分为两种情况 :
1. 当前节点在父亲节点的左边, 右旋, 之后修改父亲和祖父的颜色, 即
2. 当前节点在父亲节点的右边, 双旋, 先左旋, 交换swap(parent, cur), 再右旋,之后改变父亲和祖父的颜色, 即
- 情况三 : 父亲为红, 祖父为黑, 叔叔不存在/叔叔为黑, 父亲节点在祖父节点的右边
可以分为两种情况 :
1. 当前节点在父亲节点的右边, 左旋, 之后修改父亲和祖父的颜色, 即
2. 当前节点在父亲节点的左边, 双旋, 先右旋, 交换swap(parent, cur), 再左旋,之后改变父亲和祖父的颜色, 即
调整大概就分为上面几种情况, 具体代码后面会附上.
4. 验证
红黑树的检测分为两步:
1. 检测其是否满足二叉搜索树
2. 检测其是否满足红黑树
五. 代码
#include <iostream>
#include <time.h>
using namespace std;
enum Color
{
Red,
Black
};
template <class K,class V>
struct RBTNode
{
RBTNode(const pair<K,V>& data = pair<K,V>())
: _left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _data(data)
, _color(Red)
{}
RBTNode<K,V>* _left;
RBTNode<K,V>* _right;
RBTNode<K,V>* _parent;
pair<K,V> _data;
Color _color;
};
template <class K,class V>
class RBTRee
{
public:
typedef RBTNode<K,V> Node;
typedef Node* pNode;
RBTRee(const pair<K,V>& data = pair<K,V>())
{
_header = new Node(data);
_header->_left = _header;
_header->_right = _header;
_header->_parent = nullptr;
}
bool Insert(const pair<K, V>& data)
{
//判断是否是空树
if (_header->_parent == nullptr)
{
//创建根节点
pNode root = new Node(data);
//根节点的颜色是黑色
root->_color = Black;
//更新双向链接关系,根的父亲, 头的三个指向
root->_parent = _header;
_header->_parent = root;
_header->_left = root;
_header->_right = root;
return true;
}
//搜索合适的位置
pNode cur = _header->_parent;
pNode parent = nullptr;
while (cur)
{
//更新父节点
parent = cur;
//如果当前K大于要找的K(键值对), 向左
if (cur->_data.first > data.first)
cur = cur->_left;
else if (cur->_data.first < data.first)
cur = cur->_right;
else
return false;
//相等, 插入失败
}
//创建一个新的节点, 进行双向链接(通过K值的关系)
cur = new Node(data);
if (parent->_data.first > data.first)
{
parent->_left = cur;
}
else
parent->_right = cur;
cur->_parent = parent;
//调整 : 修改颜色, 旋转
while (cur != _header->_parent && cur->_parent->_color == Red)
{
pNode parent = cur->_parent;
pNode gparent = parent->_parent;
//0.判断父亲在祖父的哪边
//1.叔叔存在而且是红色
// 直接修改颜色, 向上更新
//2.叔叔不存在/存在而且是黑的(旋转, 结束之后调整颜色,不在向上更新)
// cur与parent不是同向, 双旋转, parent在左-->先左旋(之后交换)再右旋
// cur与parent是同向, 单旋转, 左左->右单旋, 右右->左单旋
if (gparent->_left == parent)
{
pNode uncle = gparent->_right;
if (uncle && uncle->_color == Red)
{
//调整颜色
parent->_color = uncle->_color = Black;
gparent->_color = Red;
//向上更新
cur = gparent;
}
else
{
if (parent->_right == cur)
{
RotateLeft(parent);
swap(cur, parent);
}
//右旋
RotateRight(gparent);
//调整颜色使得满足条件
parent->_color = Black;
gparent->_color = Red;
break;
}
}
else
{
pNode uncle = gparent->_left;
if (uncle && uncle->_color == Red)
{
parent->_color = uncle->_color = Black;
gparent->_color = Red;
cur = gparent;
}
else
{
if (parent->_left == cur)
{
RotateRight(parent);
swap(cur, parent);
}
RotateLeft(gparent);
parent->_color = Black;
gparent->_color = Red;
break;
}
}
}
//根节点的颜色必须为黑色
_header->_parent->_color = Black;
//调整之后根可能发生变化
_header->_left = leftMost();
_header->_right = rightMost();
return true;
}
pNode leftMost()
{
//从根节点开始
pNode cur = _header->_parent;
//一直向左边走
while (cur && cur->_left)
{
cur = cur->_left;
}
return cur;
}
pNode rightMost()
{
//从根节点开始
pNode cur = _header->_parent;
//一直向右边走
while (cur && cur->_right)
{
cur = cur->_right;
}
return cur;
}
void RotateLeft(pNode parent)
{
//左旋
pNode subR = parent->_right;
pNode subRL = subR->_left;
//改变链接关系
subR->_left = parent;
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
if (parent == _header->_parent)
{
_header->_parent = subR;
subR->_parent = _header;
}
else
{
pNode pparent = parent->_parent;
if (pparent->_left == parent)
pparent->_left = subR;
else
pparent->_right = subR;
subR->_parent = pparent;
}
parent->_parent = subR;
}
void RotateRight(pNode parent)
{
//右旋, 拿到左子树和左子树的右孩子
pNode subL = parent->_left;
pNode subLR = subL->_right;
//改变链接关系
subL->_right = parent;
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
if (parent == _header->_parent)
{
_header->_parent = subL;
subL->_parent = _header;
}
else
{
if (parent->_parent->_left == parent)
{
parent->_parent->_left = subL;
}
else
parent->_parent->_right = subL;
subL->_parent = parent->_parent;
}
parent->_parent = subL;
}
void _Inorder(pNode root)
{
//不为空
if (root)
{
_Inorder(root->_left);
cout << root->_data.first << " ";
_Inorder(root->_right);
}
}
void Inorder()
{
_Inorder(_header->_parent);
cout << endl;
}
//1. root 黑
//2. 红的不连续
//3. 任意路径黑的数量相同 (前序遍历统计数量)
bool IsBRTree()
{
//空树
if (_header->_parent == nullptr)
{
return true;
}
//root颜色
if (_header->_parent->_color == Red)
{
return false;
}
//统计一条路上黑色节点的数量
int Blackcount = 0;
pNode cur = _header->_parent;
while (cur)
{
if (cur->_color == Black)
++Blackcount;
cur = cur->_left;
}
//返回一个前序遍历的结果
return _IsBRTree(_header->_parent, Blackcount, 0);
}
bool _IsBRTree(pNode root,int Blackcount,int Curcount)
{
//空树(灰色数量是否相等)
if (root == nullptr)
{
if (Blackcount != Curcount)
return false;
return true;
}
//累加黑节点
if (root->_color == Black)
++Curcount;
//判断是否有红色连续
if (root->_parent->_color == Red && root->_color == Red)
{
return false;
}
//左右递归
return _IsBRTree(root->_left, Blackcount, Curcount)
&& _IsBRTree(root->_right, Blackcount, Curcount);
}
private:
pNode _header;
};
void testRBTree()
{
RBTRee<int, int> rbt;
int n;
cin >> n;
srand(time(nullptr));
for (int i = 0; i < n; i++)
{
int cur = rand();
cout << cur << " ";
rbt.Insert(make_pair(i, cur));
}
cout << endl;
cout << rbt.IsBRTree() << endl;
}
int main()
{
testRBTree();
system("pause");
return 0;
}
以上就是红黑树的实现, 后序会用红黑树实现Map 和 Set, 谢谢观看.