"除开隐忍当下,与展望未来。我们别无他法"
(1)红黑树;
①红黑树概念:
红黑树的概念和它名字一样。其本质就是一颗二叉树。
但在每个节点 增加一存储 颜色(enum)。
核心:确保最长路径 不超过 最短路径的两倍!
②红黑树的性质:
相对于概念,红黑树的性质是重中之重!
1.每个节点不是红色就是黑色(这相当屁话).
2.根节点(root)一定是黑色.
3.不会存在连续的红色节点.
4.每条路径的黑色节点相同.
只有保证上述四点,才能保证红黑树的一条根本性质:
最长路径 不超过 最短路径的两倍!
废话不多讲,接下来进行实现。
(2)红黑树实现
①结构框架:
这里的红黑树我们选择使用 K,V结构模拟实现。这样更方便 进行map/set的封装
//记录节点的 属性
enum Colour
{
RED,
BLACK
};
template<class K,class V>
struct RBTreeNode
{
RBTreeNode<K, V>* _left;
RBTreeNode<K, V>* _right;
RBTreeNode<K, V>* _parent;
pair<K, V> _kv;
Colour _col;
RBTreeNode(const pair<K, V> kv)
:_left(nullptr),
_right(nullptr),
_parent(nullptr),
_kv(kv),
_col(RED) //默认给红色
{}
};
template<class K,class V>
struct RBTree
{
typedef RBTreeNode<K, V> Node;
RBTree()
:_root(nullptr)
{}
Node* _root;
};
同前篇AVL树一样,先把比较简单的部分 搭建起来。对于红黑树的删除和AVL树一样,也不会提出来。
②查找+销毁;
简易:
void _Destroy(Node* root)
{
if (root == nullptr)
return;
_Destroy(root->_left);
_Destroy(root->_right);
delete root;
}
~RBTree()
{
_Destroy(_root);
_root = nullptr;
}
//查找 是 根据key值来的
Node* find(const K& k)
{
Node* cur = _root;
//和AVL 树一样
while (cur)
{
if (k > cur->_kv.first)
{
cur = cur->_right;
}
else if (k < cur->_kv.first)
{
cur = cur->_left;
}
else
{
return cur;
}
}
return nullptr;
}
③检查:
bool _CheckBalance(Node* root, int blacknum, int count)
{
//当root走到空 时 也就是统计完了 数字
if (root == nullptr)
{
if (count != blacknum)
{
cout << "黑色节点数不够" << endl;
return false;
}
return true;
}
//连续红色节点
if (root->_col == RED && root->_parent->_col == RED)
{
cout << "连续红色节点" << endl;
return false;
}
if (root->_col == BLACK)
{
count++;
}
return _CheckBalance(root->_left, blacknum, count)
&& _CheckBalance(root->_right, blacknum, count);
}
bool CheckBalance()
{
if (_root == nullptr)
return true;
if (_root->_col == RED)
{
cout << "根节点是红色" << endl;
return false;
}
int blacknum = 0;
//我们从最左边
//记录标准值
Node* left = _root;
while (left)
{
if (left->_col == BLACK)
{
++blacknum;
}
left = left->_left;
}
//其他路径的 黑色节点
int count = 0;
return _CheckBalance(_root,blacknum,count);
}
④插入:
总的来说,就是分为三种情况,且uncle节点是分析的关键。
1.p为黑色,那么不管在那里插入。都不会有影响。
2.p为红色,且uncle存在为红色;
uncle+p -->红色 g--->黑色 如果g为根就停止,不为根 继续向上调整
3.p为红色,且uncle不存在或者为红色;
单旋+变色
如果cur 与parent 不同向, 双旋+变色
此时,我们需要的左右旋 其实已经在AVL树中实现过,并且这里不需要再处理平衡因子了。
void RotateR(Node* parent)
{
Node* sub = parent->_left;
Node* subLR = sub->_right;
//让subLR去做parent的左
parent->_left = subLR;
//subLR可能不存在
if (subLR)
subLR->_parent = parent;
sub->_right = parent;
//记录 父节点 因为 sub 可能不是唯一独立的树
Node* grandparent = parent->_parent;
parent->_parent = sub;
if (parent == _root)
{
//独立树
_root = sub;
sub->_parent = nullptr;
}
else
{
//子树
//parent的 父亲仍然保留着 节点地址
if (grandparent->_left == parent)
{
grandparent->_left = sub;
}
else
{
grandparent->_right = sub;
}
//更新回来
sub->_parent = grandparent;
}
}
void RotateL(Node* parent)
{
Node* sub = parent->_right;
Node* subRL = sub->_left;
//让subRL 去做parent的右边
parent->_right = subRL;
//仍然注意避坑
if (subRL)
subRL->_parent = parent;
Node* grandparent = parent->_parent;
sub->_left = parent;
parent->_parent = sub;
if (parent == _root)
{
_root = sub;
sub->_parent = nullptr;
}
else
{
if (grandparent->_left == parent)
{
grandparent->_left = sub;
}
else
{
grandparent->_right = sub;
}
sub->_parent = grandparent;
}
}
调整红黑平衡。
//调整红黑色
while (parent && parent->_col == RED)
{
//祖父
Node* grandfather = parent->_parent;
if (grandfather->_left == parent)
{
//uncle 关键
Node* uncle = grandfather->_right;
//uncle 存在且为红
if (uncle && uncle->_col == RED)
{
uncle->_col = parent->_col = BLACK;
grandfather->_col = RED;
//继续往上处理
cur = grandfather;
parent = cur->_parent;
}
else
{
//uncle 不存在
//判断直线和折线
//此时父节点 在左边
if (cur == parent->_left)
{
//单旋
RotateR(grandfather);
//变色
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
else //右边(parent)
{
Node* uncle = grandfather->_left;
if (uncle && uncle->_col == RED)
{
uncle->_col = parent->_col = BLACK;
grandfather->_col = RED;
//继续往上处理
cur = grandfather;
parent = cur->_parent;
}
else
{
//uncle不存在
//parent在右边
if (cur == parent->_right)
{
RotateL(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
RotateR(parent);
RotateL(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break; //插入结束
}
}
}
总结
红黑树是一个效率很高的树(增删查改可以近似LogN),它可以达到近似平衡的树。
相比较于AVL树,它在翻转的次数更少。没那么严格。
在这一方面,翻转的代价很小。
'
这就是本篇的主要内容啦。感谢您的阅读
祝你好远~