前言
红黑树是一种非严格平衡的二叉搜索树,由于其查找效率很快,并且插入新节点调整比AVL树更少,所以在STL容器map和set的底层都是通过红黑树来实现的。在看这篇文章之前建议先学习AVL树插入新节点操作的实现-CSDN博客其中包括的普通二叉树的插入操作以及旋转操作,在这篇文章中不再重复
红黑树的概念
概念
红黑树,是一种二叉搜索树,但每个节点上增加一个存储位表示节点的颜色,可以使Red或Black。通过从任何一条从跟到叶子的路径上各个节点着色方式的限制。红黑树确保没有任何一条路径会比其他路径长出两倍,因而是接近平衡的。
性质
1.每个节点不是红色就是黑色
2.根节点是黑色
3.如果一个节点是红色的,则它的两个孩子节点是黑色的
4.对于每个节点,从该节点到其所有后代叶子节点的简单路径上,均包含相同数量的黑色节点
5.每个叶子节点都是黑色的(此处的叶子节点指的是空节点)
性质解读
第三点的意思是没有连续两个红色的节点,第四点的意思是,每一条路径上黑色节点的数目是相同的。这两点就能保障,最短的路径一定是全黑的,最长的路径一定是全黑全红,也就是差距最大也是二倍关系,满足接近平衡
前置代码
一样的与实现AVL树相同,我们先实现一个三叉节点,其中还带有颜色的标记,再实现普通二叉树的插入操作
#pragma once
//实现一个枚举,名为颜色,有红黑两种
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)
, _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);
cur->_col = RED;
if (parent->_kv.first < kv.first)
{
parent->_right = cur;
cur->_parent = parent;
}
else
{
parent->_left = cur;
cur->_parent = parent;
}
//之后进行检查颜色和调整
}
private:
Node* _root = nullptr;
};
不平衡情况及调整
插入新的节点默认为红色,如果出现连续两个红色的情况,要保证他的所有性质,则需要根据下面的情况进行调整
注意:cur表示插入的新节点,p表示父节点,g表示祖父节点,u表示叔叔节点
情况一
cur为红,p为红,g为黑,u存在且为红解决方法:将p,u改为黑,如果g不为根节点,g改为红,然后把g当成cur,继续向上调整
情况二
cur为红,p为红,g为黑,u不存在/u存在且为黑:
1.p为g的左孩子,cur为p的左孩子,则以g为轴点进行右单旋,后修改颜色
2.p为g的右孩子,cur为p的右孩子,则以g为轴点进行左单旋,后修改颜色
上图是1的情况,右单旋后,将p改为黑色将g改为红色,即可保证原来的各路径上的黑色节点不发生改变,且由于子树的根节点p为黑色,不需要再向上调整
情况三
cur为红,p为红,g为黑,u不存在/u存在且为黑:
1.p为g的左孩子,cur为p的右孩子,则以p为轴心,进行左单旋,就会变成情况二
2.p为g的右孩子,cur为p的左孩子,则以p为轴心,进行右单旋,就会变成情况二
补充代码
while (parent && parent->_col == RED)
{
Node* grandfater = parent->_parent;
if (parent == grandfater->_left)//p属于g的左孩子的情况
{
Node* uncle = grandfater->_right;
// 情况一 uncle存在且为红
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfater->_col = RED;
cur = grandfater;
parent = cur->_parent;
}
else
{
if (cur == parent->_left)
{
// 情况二
RotateR(grandfater);
parent->_col = BLACK;
grandfater->_col = RED;
}
else
{
// 情况三
RotateL(parent);
RotateR(grandfater);
cur->_col = BLACK;
grandfater->_col = RED;
}
break;
}
}
else //p属于g的右孩子的情况(只需与左孩子完全相反即可)
{
Node* uncle = grandfater->_left;
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfater->_col = RED;
cur = grandfater;
parent = cur->_parent;
}
else
{
// g
// p
// c
if (cur == parent->_right)
{
RotateL(grandfater);
parent->_col = BLACK;
grandfater->_col = RED;
}
else
{
// g
// p
// c
RotateR(parent);
RotateL(grandfater);
cur->_col = BLACK;
grandfater->_col = RED;
}
break;
}
}
}
_root->_col=BLACK;
return true;