目录
概念
性质
节点的定义
树的结构
Insert
1. pparent->_left == parent
1.1 uncle && uncle->_col = RED
1.2 !(uncle && uncle->_col == RED)
1.2.1 parent->_left == cur
1.2.2 parent->_right== cur
2. pparent->_right== parent
rotateR
rotateL
void InOrder()
bool isbalance()
总结
概念
- 一种搜索二叉树,确保没有一条路径会大于最小路径的两倍
性质
- 每个节点不是黑就是红
- 根节点是黑
- 红色节点的孩子都为黑色
- 每个节点的往下的每条路径的黑色节点数目相同
- 叶子节点(NIL)为nullptr节点都是黑色
节点的定义
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>
class RBTree
{
typedef RBTreeNode<K, V> node;
public:
private:
node* _root = nullptr;
};
Insert
bool Insert(const pair<K, V>& kv)
{
if (_root == nullptr)
{
_root = new node(kv);
_root->_col = BLACK;
return true;
}
node* cur = _root;
node* parent = cur;
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);
if (parent->_kv.first < kv.first)
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
cur->_parent = parent;
//改颜色,旋转
}
操作
- 先插入节点
- 默认插入的颜色为红色
- 父亲为红色就要改变
- 按照uncle是否为红色讨论
注意
- 父亲不能为空
- 父亲对于爷爷的左右会影响旋转的方向
while (parent && parent->_col == RED)
{
node* pparent = parent->_parent;
if (pparent->_left == parent)
{
node* uncle = pparent->_right;
if (uncle && uncle->_col == RED)
{
//①
}
else
{
if (parent->_left == cur)
{
//②
}
else
{
//③
}
break;
}
}
else
{
node* uncle = pparent->_left;
if (uncle && uncle->_col == RED)
{
//④
}
else
{
if (parent->_right == cur)
{
//⑤
}
else
{
//⑥
}
break;
}
}
_root->_col = BLACK;
}
1. pparent->_left == parent
1.1 uncle && uncle->_col = RED
- 只有在uncle为红的情况下,从跟开始的路径的黑色节点数才会增加1
①处代码
parent->_col = BLACK;
uncle->_col = BLACK;
pparent->_col = RED;
cur = pparent;
parent = pparent->_parent;
解释
- 肯定是parent变黑,而不是新插入的节点;如果是插入的节点变黑,那么整个路径都不对了,每次插入就一定要调整,所以开始就初始化为红色;其实也不难理解,父亲是红色,cur(newnode)也是红色,但是pparent一定是黑色,如果这时候有uncle且为红色,那么把爷爷的黑色给给parent和uncle,自己变成红色;那么此时,对于爷爷的后代节点的路径黑色节点的数目不变,若pparent就是_root那么爷爷再变成黑色(放到最后处理)
1.2 !(uncle && uncle->_col == RED)
1.2.1 parent->_left == cur
旋转解释及注意事项
②处代码
rotateR(pparent);
//cur->_col = RED;
parent->_col = BLACK;
pparent->_col = RED; //这个之前一定是黑
- 简单来说就是,旋转之后parent位置颜色变成黑色,cur 和 pparent 变成红色,pparent在旋转之前一定是黑色,因为parent是红色
1.2.2 parent->_right== cur
- 和AVLTree的旋转一模一样
③处代码
rotateL(parent);
rotateR(pparent);
cur->_col = BLACK;//这竟然写错了
pparent->_col = RED;
2. pparent->_right== parent
- 2和1可以没什么区别
else部分代码
else
{
node* uncle = pparent->_left;
if (uncle && uncle->_col == RED)
{
uncle->_col = BLACK;
parent->_col = BLACK;
pparent->_col = RED;
}
else
{
if (parent->_right == cur)
{
rotateL(pparent);
parent->_col = BLACK;
pparent->_col = RED;
}
else
{
rotateR(parent);
rotateL(pparent);
cur->_col = BLACK;
pparent->_col = RED;
}
break;
}
}
rotateR
void rotateR(node* parent)
{
node* subL = parent->_left;
node* subLR = subL->_right;
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
node* pparent = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
if (parent == _root) //其实就是pparent为空,所以下面不用担心空指针解引用
{
_root = subL;
}
else
{
if (pparent->_left == parent)
{
pparent->_left = subL;
}
else
{
pparent->_right = subL;
}
}
subL->_parent == pparent;
}
rotateL
void rotateL(node* parent)
{
node* subR = parent->_right;
node* subRL = subR->_left;
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
node* pparent = parent->_parent;
subR->_left = parent;
parent->_parent = subR;
if (parent == _root)
{
_root = subR;
}
else
{
if (pparent->_left == parent)
{
pparent->_left = subR;
}
else
{
pparent->_right = subR;
}
}
subR->_parent = pparent;
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
左右旋详解
void InOrder()
public:
void InOrder()
{
_InOrder(_root);
cout << endl;
}
private:
void _InOrder(node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_kv.first << ":" << root->_kv.second << "->" << root->_col << endl;
_InOrder(root->_right);
}
bool isbalance()
public:
bool isbalance()
{
if (_root->_col == RED)
{
return false;
}
return _isbalance(_root, 0, 0);
}
private:
bool _isbalance(node* root, int numofblack, int prev)
{
if (root == nullptr)
{
if (prev == 0)
{
prev = numofblack;
}
else
{
if (prev != numofblack)
{
return false;
}
}
return true;
}
if (root->_col == BLACK) //nullprt在上面就被刷掉了
numofblack++;
if (root->_parent && root->_parent->_col == RED && root->_col == RED)
{
return false;
}
//中序效率是不是有点低,但是从numofblack++来看好像只能这样
return _isbalance(root->_left, numofblack, prev) && _isbalance(root->_right, numofblack, prev);
}
思路
- 检查每条路径下黑色节点数目是否一样
- 检查相邻的是否都为红色,从cur回找parent即可
解释
- prev的作用是保存第一条路径的长度,且只被赋值一次
总结
- RBTree的想法比较抽象,不然AVLTree来的易懂
- RBTree的颜色改变比较简单,AVLTree双旋的时候_bf调节比较复杂,其时也不复杂
- 最需重要理解的点:RBTree的5条性质,可以保证最长路径小于最短路径的两倍