往期文章介绍了AVL树,它是一个颗平衡的搜索二叉树,当插入数据后如果导致任意结点的左右两边的高度差超过1,就会进行旋转调整,其平衡规则比较严格,所以当插入大量数据的时候就会导致多次旋转,本文介绍的红黑树也是一颗平衡搜索二叉树,它的平衡是通过控制结点的颜色来间接控制的,但是其平衡规则没有AVL树那样严格,是相对平衡的(最长的路径不超过最短路径的两倍都算平衡),所以其相对AVL树来说可以在大量插入数据的时候只需要旋转较少的次数就可以实现树的平衡。
红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或
Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路
径会比其他路径长出俩倍,因而是接近平衡的。
既然是平衡树就需要遵守一些规则来保持树的平衡,下面是红黑树必须遵守的五个规则,只有遵守这五个规则,才可以使得红黑树是平衡树!!!
思考一:为什么遵循了上面的规则就可以保证树中的最长路径不超过最短路径的两倍呢?
因为在不破坏规则的情况下,最短路径就是该路径都是全黑结点,最长路径就是一黑一红交替。极端情况下满足最长路径恰好是最短路径的两倍,不会超过两倍。
思考二:新增的结点是设为黑色还是红色呢?
考虑到规则限制,我们可以知道如果在原本平衡的树中插入新的结点的颜色为黑色,就会违反规则4(从一个结点到其所有后代叶子结点的简单路径上的黑色结点个数相同),如果在原本平衡的树中插入新的结点的颜色为红色,那么就不会违反规则4,但是有可能会违反规则3(不能存在连续的红色结点)。
综合以上两种情况,我们可以分析当一个树的结点非常多的时候那么一个结点到其所有后代的叶子结点的路径就会非常多,如果新增结点是黑色就必然违反这个规则,就需要更改每一条路径,非常难处理!!!
但是如果新增是红色就是当新增结点的父亲是红色的时候(连续红结点)才会违反规则,但是这种连续红结点的情况我们可以通过往上迭代改变结点的颜色来维护规则,总而言之就是违反规则3相对违反规则4比较好处理。所以新增的结点颜色默认设为红色的。
举例:
//结点的颜色 用枚举表示
enum color
{
RED,
BLACK,
};
template<class K,class V>
//树的结点
struct RBTreeNode
{
//三叉链
RBTreeNode<K, V>* _left;
RBTreeNode<K, V>* _right;
RBTreeNode<K, V>* _parent;//每个结点可以找到父亲
color _col;//颜色
pair<K, V> _kv;//数据
RBTreeNode(const pair<K,V>& kv)
:_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_col(RED)
,_kv(kv)
{
}
};
template<class K,class V>
class RBTree
{
public:
typedef RBTreeNode<K,V> Node;
typedef __RBTree_Iterator<T, T&, T*> iterator;
typedef __RBTree_Iterator<T,const T&,const T*> const_iterator;//迭代器
RBTree()
:_root(nullptr)
{}
RBTree(const RBTree<K, V>& t)//拷贝构造
{
_root = copyNode(t._root);
}
iterator begin();
const_iterator begin()const {}
iterator end();
const_iterator endn()const{}
//左右旋转
void RotateL(Node* parent);
void RotateR(Node* parent);
//主要是掌握插入数据构建红黑树
bool insert(const pair<K,V>& kv);
size_t getMaxHight();
size_t getMinHight();
bool checkIsRBTree();
private:
Node* _root;
}
学习红黑树主要是学插入数据的过程中的各种导致不平衡的情况该如何调整,使得插入数据前后都是平衡的树,不违法红黑树遵循的规则。
这里称插入结点(c表示)的父亲结点的兄弟结点为叔叔结点(u表示),插入结点的父亲的父亲为祖父结点(g表示)。
处理:将p变黑,u变黑,g变红(如果g是根结点就将其颜色改成黑)再往上迭代器处理(即第一次变色后可能会影响上层的结点,就需要往上迭代处理,如果有违反的情况就处理)
为什么需要迭代往上修改呢?
如下图,出现连续的红色结点后进行了改色,这可能会影响上层的结点,这里使g变成了红色g的父节点也是红色,就又出现了连续的红结点,就还需要处理,所以要往上迭代处理如果不符合规则就对其进行处理,处理到符合所有规则为止!!!
叔叔不存在的情况也有四种
满足c为红 p为红 g为黑
处理方式:对g进行右单旋,再把p变成黑色,g变红色
处理方式:对p进行左单旋,然后变成第一种情况(/斜线型),进行处理。
处理方式:对g进行左单旋,再把p变成黑色,g变红色
处理方式:对p进行右单旋,然后变成第一种情况(\斜线型),进行处理。
叔叔存在且为黑的情况是由叔叔存在且为红的情况转化而来的,下面的图都是包含叔叔存在且为红的情况,处理完叔叔存在且为红后就有可能会形成叔叔存在且为黑的情况,以下就是所有的情况,其中的△是代表空或者各种子树(也就是说△可以拓展开来,只要拓展开后不违反规则就可以!)
这里涉及树的旋转 可以参考前几期的AVL树的博客 里面有旋转的详细解析
满足c为红 p为红 g为黑
处理方式:1、处理完叔叔存在且为红的情况后转化为叔叔存在且为黑的情况。
2、对g进行右单旋,再把p变成黑色,g变红色
处理方式:1、处理完叔叔存在且为红的情况后转化为叔叔存在且为黑的情况。
2、对p进行左单旋,将折线型(<)转化为斜线型(/),转化为上面的情况1处理
处理方式:1、处理完叔叔存在且为红的情况后转化为叔叔存在且为黑的情况。
2、对g进行左单旋,再把p变成黑色,g变红色
处理方式:1、处理完叔叔存在且为红的情况后转化为叔叔存在且为黑的情况。
2、对p进行右单旋,将折线型(>)转化为斜线型(\),转化为上面的情况3处理
插入函数的代码:
bool insert(const pair<K,V>& kv)
{
//第一次插入的情况 新插入结点(红色)做根结点 然后得把其颜色改成黑
if (_root == nullptr)
{
_root = new Node(kv);
_root->_col = BLACK;
return true;
}
else
{
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已经为空了 直接将新结点赋值给cur
cur = new Node(kv);
cur->_col = RED;
//链接新增结点和其父节点
if (cur->_kv.first < parent->_kv.first)
{
parent->_left = cur;
}
else
{
parent->_right = cur;
}
cur->_parent = parent;
//调整红黑树 维持规则
while (parent&&parent->_col==RED)
{
Node* grandfather = parent->_parent;
Node* uncle = nullptr;
if (grandfather->_left == parent)
{
uncle = grandfather->_right;
}
else
{
uncle = grandfather->_left;
}
if (parent&&parent->_col== RED && uncle && uncle->_col == RED)//叔叔存在且为红
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
cur = grandfather;//迭代往上调整
parent = cur->_parent;
}
else if (parent&&parent->_col==RED&&(uncle == nullptr||(uncle!=nullptr&&uncle->_col==BLACK)) )//叔叔不存在或存在且为黑
{
if (grandfather->_left==parent&&cur==parent->_right)//叔叔不存在
{
// g
// p
// c
RotateL(parent);
swap(parent, cur);
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else if (grandfather->_left == parent && cur == parent->_left)
{
// g
// p
// c
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else if (grandfather->_right == parent && cur == parent->_left)
{
// g
// p
// c
RotateR(parent);
swap(parent, cur);
RotateL(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else if (grandfather->_right == parent && cur == parent->_right)
{
// g
// p
// c
RotateL(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
_root->_col = BLACK;
return true;
}
}
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL)
{
subRL->_parent = parent;
}
Node* ppnode = parent->_parent;
subR->_left = parent;
parent->_parent = subR;
if (parent == _root)
{
_root = subR;
_root->_parent = nullptr;
}
else
{
if (ppnode->_left == parent)
{
ppnode->_left = subR;
}
else if(ppnode->_right==parent)
{
ppnode->_right = subR;
}
else
{
cout << "内部错误!" << endl;
assert(false);
}
subR->_parent = ppnode;
}
}
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR)
{
subLR->_parent = parent;
}
Node* ppnode = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
if (parent == _root)
{
_root = subL;
_root->_parent = nullptr;
}
else
{
if (ppnode->_left == parent)
{
ppnode->_left = subL;
}
else if (ppnode->_right == parent)
{
ppnode->_right = subL;
}
else
{
cout << "内部错误!" << endl;
assert(false);
}
subL->_parent = ppnode;
}
}
bool checkIsRBTree()
{
if (_root == nullptr)//空树也是红黑树
{
return true;
}
if (_root->_col != BLACK)
{
cout << "根结点不为黑!" << endl;
return false;
}
Node* cur = _root;
size_t standardBlackCount = 0;
while (cur)
{
if (cur->_col == BLACK)
{
standardBlackCount++;
}
cur = cur->_left;
}
size_t k = 0;
return _checkIsRBTree(_root, standardBlackCount, k);
}
//传一个最短基准值 判断每条路径是否超过其两倍
bool _checkIsRBTree(Node* root,size_t standardCount,size_t blackCount)
{
if (root == nullptr)
{
if (standardCount != blackCount)
{
cout << "不符合每条路径上的黑结点数目相同!" << endl;
return false;
}
return true;
}
//统计当前路径的黑结点的数量
if(root->_col==BLACK)
blackCount++;
Node* parent = root->_parent;
if (root->_col == RED && parent && parent->_col == RED)
{
cout << "存在连续红结点!" << endl;
return false;
}
return _checkIsRBTree(root->_left,standardCount,blackCount) && _checkIsRBTree(root->_right,standardCount,blackCount);
}
RBTree(const RBTree<K, V>& t)
{
_root = copyNode(t._root);
}
Node* copyNode(Node* root)
{
if (root == nullptr)
{
return nullptr;
}
Node* newroot = new Node(root->_kv);
newroot->_left = copyNode(root->_left);
newroot->_left = copyNode(root->_right);
return newroot;
}
获取树的最小和最大高度
size_t getMaxHight()
{
return _getMaxHight(_root);
}
size_t _getMaxHight(Node* root)
{
if (root == nullptr)
{
return 0;
}
size_t LMH = _getMaxHight(root->_left);
size_t RMH = _getMaxHight(root->_right);
return LMH > RMH ? LMH + 1 : RMH + 1;
}
size_t getMinHight()
{
return _getMinHight(_root);
}
size_t _getMinHight(Node* root)
{
if (root == nullptr)
{
return 0;
}
size_t LMH = _getMinHight(root->_left);
size_t RMH = _getMinHight(root->_right);
return LMH < RMH ? LMH + 1 : RMH + 1;
}
operator-- 就是类比operator++ 即可推出,这里就不多bb了 。
template<class T,class Ref,class Ptr>
struct __RBTree_Iterator
{
typedef __RBTree_Iterator<T, Ref, Ptr> self;
typedef RBTreeNode<T> Node;
__RBTree_Iterator(Node* root)
:_root(root)
{
}
Ref operator*()//这里是无参的返回的额是当前迭代器对象的数据
{
return _root->_data;
}
Ptr operator->()
{
return &(_root->_data);
}
self& operator=(const self& it)
{
_root = it._root;
}
self& operator++()
{
if (_root->_right == nullptr)
{
Node* cur = _root;
Node* parent = _root->_parent;
while (parent&&parent->_left != cur)//要保证最后一个结点的
{
cur = cur->_parent;
parent = parent->_parent;
}
_root = parent;
}
else
{
Node* right = _root->_right;
while (right->_left)
{
right = right->_left;
}
_root = right;
}
return *this;
}
self& operator++(int)
{
//self tmp = *this;
self tmp(*this);
++ *this;
return tmp;
}
self& operator--()
{
if (_root->_left==nullptr)
{
Node* cur = _root;
Node* parent = _root->_parent;
while (parent&&parent->_right != cur)
{
cur = cur->_parent;
parent = parent->_parent;
}
_root = parent;
}
else
{
Node* left = _root->_left;
while (left->_right)
{
left = left->_right;
}
_root = left;
}
return *this;
}
self& operator--(int)
{
//self tmp = *this;
self tmp(*this);
-- *this;
return tmp;
}
bool operator==(const self& it)const
{
return _root == it._root;
}
bool operator!=(const self& it)const
{
return _root != it._root;
}
Node* _root;
};