目录
红黑树简介
红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或
Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路
径会比其他路径长出俩倍,因而是接近平衡的。
红黑树的性质
1. 每个结点不是红色就是黑色
2. 根节点是黑色的
3. 如果一个节点是红色的,则它的两个孩子结点是黑色的
4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点
5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)
红黑树的实现
节点的实现
默认插入的节点是红色。
如果默认插入的节点是黑色,就会造成整个树都混乱。
enum Color
{
RED,
BLACK
};
// set ->key
// map ->key/value
template<class T>
struct RBTreeNode
{
RBTreeNode<T>* _left;
RBTreeNode<T>* _right;
RBTreeNode<T>* _parent;
T _data;
Color _color;
RBTreeNode(const T& data)
:_left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _data(data)
, _color(RED) //默认插入红色节点
{}
};
迭代器的实现
template<class T, class Ref, class Ptr>
struct __RBTreeIterator
{
typedef RBTreeNode<T> Node;
typedef __RBTreeIterator<T, Ref, Ptr> Self;
Node* _node;
__RBTreeIterator(Node* node)
:_node(node)
{}
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
//Self& operator--()
Self& operator++()
{
if (_node->_right) //如果右树存在,那就按照中序访问右树,得到第一个节点
{
// 下一个就是右子树的最左节点
Node* cur = _node->_right;
while (cur->_left)
{
cur = cur->_left;
}
_node = cur;
}
else
{
// 左子树 根 右子树
// 右为空,找孩子是父亲左的那个祖先
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_right)
{
cur = parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
bool operator!=(const Self& s)
{
return _node != s._node;
}
bool operator==(const Self& s)
{
return _node == s._node;
}
};
其中ref与ptr用来区分const迭代器与非const迭代器
特别注意operator++操作。按照中序的顺序,当存在右节点时,去访问右节点的中序第一个节点。
不存在右节点时,去访问grand节点,知道父节点不存在,或者父节点不再是右子树
树的实现
// set->RBTree<K, K, SetKeyOfT> _t;
// map->RBTree<K, pair<K, T>, MapKeyOfT> _t;
template<class K, class T, class KeyOfT> //KeyOfT: functor to get key of T(目的是为了得到键值,而不是类型)
class RBTree
{
typedef RBTreeNode<T> Node;
public:
typedef __RBTreeIterator<T, T&, T*> iterator;
typedef __RBTreeIterator<T, const T&, const T*> const_iterator;
iterator begin()
{
//返回的不是根节点,而是第一个节点
Node* cur = _root;
while (cur && cur->_left)
{
cur = cur->_left;
}
return iterator(cur);
}
const_iterator begin() const
{
//返回的不是根节点,而是第一个节点
Node* cur = _root;
while (cur && cur->_left)
{
cur = cur->_left;
}
return const_iterator(cur);
}
iterator end()
{
return iterator(nullptr);
}
const_iterator end() const
{
return const_iterator(nullptr);
}
pair<Node*, bool> Insert(const T& data) //返回的是Node*与布尔值的pair,布尔值表示是否插入成功,Node*可以隐式类型构造iterator
{
if (_root == nullptr)
{
_root = new Node(data);
_root->_color = BLACK; //修正为黑色节点
return make_pair(_root, true);
}
Node* parent = nullptr;
Node* cur = _root;
KeyOfT kot;
while (cur)
{
if (kot(date) > kot(cur->_data)) //对象() 这是一种仿函数调用的方式,这个对象可以是匿名对象,也可以是有名对象
{
parent = cur;
cur = cur->_right;
}
else if (kot(date) < kot(cur->_data))
{
parent = cur;
cur = cur->_left;
}
else
{
return make_pair(cur, false); //已经存在相同的键值,不插入
}
}
//cur为空,说明找到了插入位置
cur = new Node(data); //不需要调整颜色,默认为红色
Node* newnode = cur;
cur->_parent = parent;
if (kot(data) > kot(parent->_data))
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
//进行调整
while (parent && parent->_color == RED)
{
Node* grand = parent->_parent;
if (parent == grand->_left) //parent是左孩子
{
// g
// p u
// c
Node* uncle = grand->_right;
//parent两个位置可能,cur也是,所以一共四种
if (uncle && uncle->_color == RED) //叔叔是红色
{
parent->_color = BLACK;
uncle->_color = BLACK;
grand->_color = RED;
// 继续往上更新处理
cur = grand;
parent = cur->_parent;
}
else //叔叔不存在,或者是黑色
{
if (cur == parent->_left) //cur是parent的左孩子
{
// 单旋
// g
// p
// c
RotateR(grand); //旋转
parent->_color = BLACK;
grand->_color = RED;
}
else //cur是parent的右孩子
{
// 双旋 (先左旋,再右旋)
// g
// p
// c
RotateL(parent);
RotateR(grand);
cur->_col = BLACK;
grand->_col = RED;
}
break; //旋转之后降低了高度,恢复到了插入之前的水平,可以退出循环
}
}
else // parent == grandfather->_right
{
// g
// u p
// c
//
Node* uncle = grand->_left;
if (uncle && uncle->_col == RED)
{
// 变色
parent->_col = uncle->_col = BLACK;
grand->_col = RED;
// 继续往上处理
cur = grand;
parent = cur->_parent;
}
else //叔叔不存在,或者是黑色
{
if (cur == parent->_right)
{
// 单旋
// g
// u p
// c
RotateL(grand);
parent->_col = BLACK;
grand->_col = RED;
}
else
{
// 双旋
// g
// u p
// c
RotateR(parent);
RotateL(grand);
cur->_col = BLACK;
grand->_col = RED;
}
break;
}
}
}
_root->_col = BLACK;
return make_pair(newnode, true); //cur已经发生修改,所以返回的是newnode
}
bool IsBalance()
{
if (_root == nullptr)
return true;
if (_root->_col == RED)
return false;
//参考值
int refVal = 0;
Node* cur = _root;
while (cur)
{
if (cur->_col == BLACK)
{
++refVal;
}
cur = cur->_left;
}
int blacknum = 0;
return Check(_root, blacknum, refVal);
}
size_t Size()
{
return _Size(_root);
}
Node* Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (cur->_kv.first < key)
{
cur = cur->_right;
}
else if (cur->_kv.first > key)
{
cur = cur->_left;
}
else
{
return cur;
}
}
return nullptr;
}
pirvate:
size_t _Size(Node* root)
{
if (root == nullptr)
return 0;
return _Size(root->_left)
+ _Size(root->_right) + 1;
}
//处理好复杂的关系线
void RotateL(Node* parent)
{
Node* SubR = parent->_right;
Node* SubRL = SubR->_left;
parent->_right = subRL;
if (SubRL)
{
SubRL->_parent = parent;
}
subR->_left = parent;
parent->_parent = subR;
Node* parentParent = parent->_parent;
if (_root == parent)
{
_root = subR;
subR->_parent = nullptr;
}
else
{
if (parentParent->_left == parent)
{
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 (_root == parent)
{
_root = subL;
subL->_parent = nullptr;
}
else
{
if (parentParent->_left == parent)
{
parentParent->_left = subL;
}
else
{
parentParent->_right = subL;
}
subL->_parent = parentParent;
}
}
// 根节点->当前节点这条路径的黑色节点的数量
bool Check(Node* root, int blacknum, const int refVal)
{
if (root == nullptr)
{
//cout << balcknum << endl;
if (blacknum != refVal)
{
cout << "存在黑色节点数量不相等的路径" << endl;
return false;
}
return true;
}
if (root->_col == RED && root->_parent->_col == RED)
{
cout << "有连续的红色节点" << endl;
return false;
}
if (root->_col == BLACK)
{
++blacknum;
}
return Check(root->_left, blacknum, refVal)
&& Check(root->_right, blacknum, refVal);
}
private:
Node* _root;
};
}
检测新节点插入后,红黑树的性质是否造到破坏
因为
新节点的默认颜色是红色
,因此:如果
其双亲节点的颜色是黑色,没有违反红黑树任何
性质
,则不需要调整;但
当新插入节点的双亲节点颜色为红色时,就违反了性质三不能有连
在一起的红色节点
,此时需要对红黑树分情况来讨论:
约定
:cur
为当前节点,
p
为父节点,
g
为祖父节点,
u
为叔叔节点
(由于父节点与子节点的位置不确定,因此需要分别讨论到底是左孩子还是右孩子)
情况一
:
cur
为红,
p
为红,
g
为黑,
u
存在且为红
解决方式:将
p,u
改为黑,
g
改为红,然后把
g
当成
cur
,继续向上调整。
情况二
:
cur
为红,
p
为红,
g
为黑,
u
不存在
/u
存在且为黑
p
为
g
的左孩子,
cur
为
p
的左孩子,则进行右单旋转;相反,
p
为
g
的右孩子,
cur
为
p
的右孩子,则进行左单旋转
p
、
g
变色
--p
变黑,
g
变红
情况三
:
cur
为红,
p
为红,
g
为黑,
u
不存在
/u
存在且为黑
p
为
g
的左孩子,
cur
为
p
的右孩子,则针对
p
做左单旋转;相反,
p
为
g
的右孩子,
cur
为
p
的左孩子,则针对
p
做右单旋转
则转换成了情况
2
总之,类似AVL树的旋转,看插入的位置,分为左旋、右旋、先左后右、先右后左四种情况,旋转完成之后需要变色,同时break
对于旋转算法而言,重难点是需要正确理清节点之间连接的顺序。同时需要判断特定的位置原本是不是空(防止出现空指针的解引用)。旋转完成之后,cur变成新的父节点,此时需要与grand节点判断位置,进行连接。
红黑树的检测
红黑树的检测分为两步:
1. 检测其是否满足二叉搜索树(中序遍历是否为有序序列)
2. 检测其是否满足红黑树的性质
我们算出最左路的黑色节点作为参照。遍历其他路,比较是否数据一致。
同时检测是否出现连续的红色节点。
bool Check(Node* root, int blacknum, const int refVal)
{
if (root == nullptr)
{
//cout << balcknum << endl;
if (blacknum != refVal)
{
cout << "存在黑色节点数量不相等的路径" << endl;
return false;
}
return true;
}
if (root->_col == RED && root->_parent->_col == RED)
{
cout << "有连续的红色节点" << endl;
return false;
}
if (root->_col == BLACK)
{
++blacknum;
}
return Check(root->_left, blacknum, refVal)
&& Check(root->_right, blacknum, refVal);
}
红黑树的应用
1. C++ STL库 -- map/set、mutil_map/mutil_set
2. Java 库
3. linux内核
4. 其他一些库