一.红黑树概念
红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。
最长路径 <2 * 最短路径
二.红黑树性质
1. 每个结点不是红色就是黑色
2. 根节点是黑色的
3. 可以有连续的黑色节点,但不能有连续的红色节点。
4. 从根到null的每个路径上的黑色节点个数相同。
5. 每个空节点当成黑色节点。
三.函数实现
1.节点实现
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)
{}
};
用enum枚举来定义颜色。
2.插入
首先对插入的节点定义颜色,是黑色还是红色?
如果定义为黑色,那么为了保持每条路径的黑色节点个数一致,我们还需对所有路径进行调整。而定义成红色,如果连的是黑色节点这不需要调整。就算是红色节点连续了,进行调整的范围不一定是对整颗子树。
所以插入节点定义为红色更好调整。
和AVL树插入一致,第一步都是去找对应的位置,小于在左边,大于在右边。
注意:插入节点后,要定义颜色。
bool Insert(const pair<K, V> kv)
{
//没有节点直接插入
if (_root == nullptr)
{
_root = new Node(kv);
//根节点一定为黑色
_root->_col = BLACK;
return true;
}
//找对应的位置 有相同的值返回false
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (kv.first > cur->_kv.first)
{
parent = cur;
cur = cur->_right;
}
else if (kv.first < cur->_kv.first)
{
parent = cur;
cur = cur->_left;
}
else return false;
}
//判断该位置是左孩子还是右孩子
cur = new Node(kv);
cur->_col = RED;
if (kv.first > parent->_kv.first) parent->_right = cur;
else parent->_left = cur;
cur->_parent = parent;
(cur插入节点 p插入节点的父母节点 g是p的父母节点 u是g的另一个孩子)
需要调整的情况分为
一.u存在且为红
1.cur红,p红,g黑,u存在且为红
二.u不存在/存在且为黑 这又要根据cur在p的位置分为单旋 双旋
1.cur红,p红,g黑,u不存在/存在且为黑(g p cur连线是直线,单旋)
2.cur红,p红,g黑,u不存在/存在且为黑(g p cur连线是曲线,双旋)
再根据u节点位置,又分为u在右边 u在左边两种情况
while (parent&&parent->_col==RED)
{
Node* grandparent = parent->_parent;
//u位置右边
if (grandparent->_left == parent)
{
Node* uncle = grandparent->_right;
//一.u存在且为红 变色+上调
if (uncle && uncle->_col == RED)
{
grandparent->_col = RED;
parent->_col = uncle->_col = BLACK;
cur = grandparent;
parent = cur->_parent;
}
//二.u不存在/存在为黑色
else
{
//1.cur 位于p的左边 对g右单旋+变色 此时cur和parent都为红色 (这种情景是由情况1.u存在且为红 变色+上调出现的)
if (cur == parent->_left)
{
RotateR(grandparent);
parent->_col = BLACK;
grandparent->_col = RED;
}
//2.cur 位于p的右边 对p左旋 再对g右单旋+变色
else
{
RotateL(parent);
RotateR(grandparent);
cur->_col = BLACK;
grandparent->_col = RED;
}
//处理完后 根节点为黑 各路径黑节点数相等
break;
}
}
//u位于左边
else
{
Node* uncle = grandparent->_left;
//1.u存在且为红 变色+上调
if (uncle && uncle->_col == RED)
{
grandparent->_col = RED;
parent->_col = uncle->_col = BLACK;
cur = grandparent;
parent = cur->_parent;
}
//2.u不存在/存在为黑色
else
{
//1.cur 位于p的右边 对g左单旋+变色 此时cur和parent都为红色 (这种情景是由情况1.u存在且为红 变色+上调出现的)
if (cur == parent->_right)
{
RotateR(grandparent);
parent->_col = BLACK;
grandparent->_col = RED;
}
//2.cur 位于p的右边 对p左旋 再对g右单旋+变色
else
{
RotateR(parent);
RotateL(grandparent);
cur->_col = BLACK;
grandparent->_col = RED;
}
//处理完后 根节点为黑 各路径黑节点数相等
break;
}
}
}
//第一种情况u存在为红 g为根,p为空 循环结束,此时g根节点为红色 需要变色
_root->_col = BLACK;
return true;
}
1.cur红,p红,g黑,u存在且为红
解决方法:变色+上调
1.p u变黑 g变红 每条路径的黑色节点不变
2.上调。1.g就是根,没有父母节点。因为根必须为黑色,g变黑。
2.g有父母节点,让cur=g p=cur->_parent 继续上调。
2.cur红,p红,g黑,u不存在/存在且为黑(g p cur连线是直线,单旋)
左图u存在且为黑色所以是经过情况一(u为红)cur变色后导致的情况。
此时单靠变色无法解决,对g进行右旋使黑色节点相等。
解决方法:单旋+变色
eg.
3.cur红,p红,g黑,u不存在/存在且为黑(g p cur连线是曲线,双旋)
解决方法:双旋+变色
总结:就是看u节点是否存在且为红色,是就只需要变色+上调。
反之就需要旋转+变色。具体单旋还是双旋就要看g p cur是否连成一条直线,能就单旋。
完整代码:
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)
{}
};
template<class K, class V>
class RBTree
{
typedef RBTreeNode<K, V> Node;
public:
RBTree() = default;
RBTree(const RBTree<K, V>& t)
{
_root = Copy(t._root);
}
RBTree<K, V>& operator=(RBTree<K, V> t)
{
swap(_root, t._root);
return *this;
}
~RBTree()
{
Destroy(_root);
_root = nullptr;
}
bool Insert(const pair<K, V> kv)
{
//没有节点直接插入
if (_root == nullptr)
{
_root = new Node(kv);
_root->_col = BLACK;
return true;
}
//找对应的位置 有相同的值返回false
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (kv.first > cur->_kv.first)
{
parent = cur;
cur = cur->_right;
}
else if (kv.first < cur->_kv.first)
{
parent = cur;
cur = cur->_left;
}
else return false;
}
//判断该位置是左孩子还是右孩子
cur = new Node(kv);
cur->_col = RED;
if (kv.first > parent->_kv.first) parent->_right = cur;
else parent->_left = cur;
cur->_parent = parent;
//调整
while (parent&&parent->_col==RED)
{
Node* grandparent = parent->_parent;
//u位置右边
if (grandparent->_left == parent)
{
Node* uncle = grandparent->_right;
//1.u存在且为红 变色+上调
if (uncle && uncle->_col == RED)
{
grandparent->_col = RED;
parent->_col = uncle->_col = BLACK;
cur = grandparent;
parent = cur->_parent;
}
//2.u不存在/存在为黑色
else
{
//1.cur 位于p的左边 对g右单旋+变色 此时cur和parent都为红色 (这种情景是由情况1.u存在且为红 变色+上调出现的)
if (cur == parent->_left)
{
RotateR(grandparent);
parent->_col = BLACK;
grandparent->_col = RED;
}
//2.cur 位于p的右边 对p左旋 再对g右单旋+变色
else
{
RotateL(parent);
RotateR(grandparent);
cur->_col = BLACK;
grandparent->_col = RED;
}
//处理完后 根节点为黑 各路径黑节点数相等
break;
}
}
//u位于左边
else
{
Node* uncle = grandparent->_left;
//1.u存在且为红 变色+上调
if (uncle && uncle->_col == RED)
{
grandparent->_col = RED;
parent->_col = uncle->_col = BLACK;
cur = grandparent;
parent = cur->_parent;
}
//2.u不存在/存在为黑色
else
{
//1.cur 位于p的右边 对g左单旋+变色 此时cur和parent都为红色 (这种情景是由情况1.u存在且为红 变色+上调出现的)
if (cur == parent->_right)
{
RotateR(grandparent);
parent->_col = BLACK;
grandparent->_col = RED;
}
//2.cur 位于p的右边 对p左旋 再对g右单旋+变色
else
{
RotateR(parent);
RotateL(grandparent);
cur->_col = BLACK;
grandparent->_col = RED;
}
//处理完后 根节点为黑 各路径黑节点数相等
break;
}
}
}
//第一种情况u存在为红 g为根,p为空 循环结束,此时g根节点为红色 需要变色
_root->_col = BLACK;
return true;
}
void Inorder()
{
_Inorder(_root);
return;
}
private:
Node* _root = nullptr;
void _Inorder(Node* root)
{
if (root == nullptr) return;
_Inorder(root->_left);
cout << root->_kv.first << "->" << root->_kv.second << ' ';
_Inorder(root->_right);
}
Node* Copy(Node* root, Node* parent)
{
if (root == nullptr) return nullptr;
Node* cur = new Node(root->_kv);
cur->_parent = parent;
cur->_left = Copy(root->_left, cur);
cur->_right = Copy(root->_right, cur);
return cur;
}
void Destroy(Node* root)
{
if (root == nullptr) return;
Destroy(root->_left);
Destroy(root->_right);
delete root;
}
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
Node* parentP = parent->_parent;
parent->_right = subRL;
if (subRL) subRL->_parent = parent;
subR->_left = parent;
parent->_parent = subR;
if (parentP == nullptr)
{
_root = subR;
subR->_parent = nullptr;
}
else
{
subR->_parent = parentP;
if (parentP->_left == parent) parentP->_left = subR;
else parentP->_right = subR;
}
}
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
Node* parentP = parent->_parent;
parent->_left = subLR;
if (subLR) subLR->_parent = parent;
subL->_right = parent;
parent->_parent = subL;
if (parentP == nullptr)
{
_root = subL;
subL->_parent = nullptr;
}
else
{
subL->_parent = parentP;
if (parentP->_left == parent) parentP->_left = subL;
else parentP->_right = subL;
}
}
};