hello,各位小伙伴,本篇文章跟大家一起学习《C++:红黑树(RBT)》,感谢大家对我上一篇的支持,如有什么问题,还请多多指教 !
红黑树(RBT)的概念
红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。
红黑树(RBT)的性质:
- 每个结点不是红色就是黑色
- 根节点是黑色的
- 如果一个节点是红色的,则它的两个孩子结点是黑色的
- 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点
- 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)
总结就是三个规则:
- 根节点是黑色的
- 每条路径的黑色节点数目相同
- 不能有连续的2个红色节点
满足以上性质,红黑树(RBT)就能保证其最长路径中节点个数不会超过最短路径节点个数的两倍。
假设最短路径寻找节点时间复杂度为O(
l
o
g
2
n
log_2 n
log2n)。
那么最长路径寻找节点时间复杂度为O(
l
o
g
2
2
n
log_2 2n
log22n)。
实质上时间差不了多少。
红黑树节点的定义
enum Color // 节点颜色
{
RED,
BLACK
};
template<class T>
struct RBTreeNode
{
RBTreeNode<T>(const T& data = T(),Color col = RED)
:_data(data)
, _parent(nullptr)
, _left(nullptr)
, _right(nullptr)
, _col(col) // 新增节点默认为红色
{}
T _data;
RBTreeNode<T>* _parent;
RBTreeNode<T>* _left;
RBTreeNode<T>* _right;
Color _col;
};
相比于AVL树也就是将平衡因子改为节点颜色。这样做的目的是为了减少结点的旋转。
为什么新增节点默认为红色呢?
因为在RBT规则里:**每条路径的黑色节点数目必须相同。**当出现2个红色节点时进行处理。
RBT结构
// KeyOfVal为伪函数,当传入的值为pair时,可以确定值是什么
template<class K, class T, class KeyOfVal>
class RBTree
{
typedef RBTreeNode<T> Node;
public:
typedef RBTreeIterator<T, T&, T*> Iterator;// 迭代器
typedef RBTreeIterator<const T, const T&, const T*> Const_Iterator;
RBTree() = default;// 强制生成默认构造
RBTree(const RBTree& t)// 拷贝构造
{
_root = Copy(t._root);
}
private:
// 旋转操作和平衡二叉树一样
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
Node* parentParent = parent->_parent;
subR->_left = parent;
parent->_parent = subR;
if (parentParent == nullptr)// 更改根节点
{
_root = subR;
subR->_parent = nullptr;
}
else
{
if (parent == parentParent->_left)
{
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 (parentParent == nullptr)// 更改根节点
{
_root = subL;
subL->_parent = nullptr;
}
else
{
if (parent == parentParent->_left)
{
parentParent->_left = subL;
}
else
{
parentParent->_right = subL;
}
subL->_parent = parentParent;
}
}
void RotateRL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
RotateR(parent->_right);
RotateL(parent);
}
void RotateLR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
RotateL(parent->_left);
RotateR(parent);
}
Node* Copy(Node* root)
{
if (root == nullptr)
return nullptr;
Node* newRoot = new Node(root->_key, root->_value);
newRoot->_left = Copy(root->_left);
newRoot->_right = Copy(root->_right);
return newRoot;
}
Node* _root;
insert插入操作
当出现两个连续的红色节点时分两种情况:
- 叔叔节点存在且为红色
- 叔叔存在且为黑色或者叔叔不存在(叔叔为空)
- 当叔叔节点存在且为红色时,直接让父节点和叔叔节点变为黑色,祖先节点(gparent)变为红色,继续向上调整
- 当叔叔节点存在且为黑色或者叔叔节点不存在(空)时,旋转处理,旋转后调整颜色
对于旋转处理与AVL树一样,可看C++:平衡搜索二叉树(AVL)
// 返回pair<Iterator, bool>
pair<Iterator, bool> Insert(const T& data)
{
KeyOfVal kt;
if (_root == nullptr)// 创建根节点
{
_root = new Node(data);
_root->_col = BLACK;
return make_pair(Iterator(_root, _root), true);
}
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (kt(cur->_data) < kt(data))
{
parent = cur;
cur = cur->_right;
}
else if (kt(cur->_data) > kt(data))
{
parent = cur;
cur = cur->_left;
}
else
// 发现该值存在,插入失败
return make_pair(Iterator(cur, cur), false);
}
cur = new Node(data);
Node* newnode = cur;
// cur->_col = RED; 默认新增节点为红色
if (kt(data) > kt(parent->_data))
parent->_right = cur;
else
parent->_left = cur;
cur->_parent = parent;
// 出现两个连续的红色节点,进行操作
while (parent && parent->_col == RED)
{
Node* gparent = parent->_parent;
if (parent == gparent->_left)
{
Node* uncle = gparent->_right;
if (uncle && uncle->_col == RED)
{
// u存在且为红 ->变色再继续往上处理
parent->_col = uncle->_col = BLACK;
gparent->_col = RED;
cur = gparent;
parent = cur->_parent;
}
else
{
// u存在且为黑或不存在 ->旋转+变色
if (cur == parent->_left)
{
RotateR(gparent);
parent->_col = BLACK;
gparent->_col = RED;
}
else
{
RotateLR(gparent);
cur->_col = BLACK;
gparent->_col = RED;
}
break;
}
}
else
{
Node* uncle = gparent->_left;
if (uncle && uncle->_col == RED)
{
// u存在且为红 ->变色再继续往上处理
parent->_col = uncle->_col = BLACK;
gparent->_col = RED;
cur = gparent;
parent = cur->_parent;
}
else
{
// u存在且为黑或不存在 ->旋转+变色
if (cur == parent->_right)
{
RotateL(gparent);
parent->_col = BLACK;
gparent->_col = RED;
}
else
{
RotateRL(gparent);
cur->_col = BLACK;
gparent->_col = RED;
}
break;
}
}
}
// 由于并不知道根节点会不会被改为红色,所以直接将根节点改为黑色
_root->_col = BLACK;
return make_pair(Iterator(newnode, _root), true);
}
迭代器的实现
template<class T, class Ref, class Ptr>
struct RBTreeIterator
{
typedef RBTreeNode<T> Node;
typedef RBTreeIterator<T, Ref, Ptr> Self;
Node* _node;
Node* _root;
RBTreeIterator(Node* node, Node* root)
:_node(node)
, _root(root)
{}
Self& operator++()
{
if (_node->_right)
{
// 右不为空,右子树最左节点就是中序第一个
Node* leftMost = _node->_right;
while (leftMost->_left)
{
leftMost = leftMost->_left;
}
_node = leftMost;
}
else
{
// 孩子是父亲左的那个祖先
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_right)
{
cur = parent;
parent = cur->_parent;
}
_node = parent;
}
return *this;
}
Self& operator--()
{
if (_node == nullptr) // end()
{
// --end(),特殊处理,走到中序最后一个节点,整棵树的最右节点
Node* rightMost = _root;
while (rightMost && rightMost->_right)
{
rightMost = rightMost->_right;
}
_node = rightMost;
}
else if (_node->_left)
{
// 左子树不为空,中序左子树最后一个
Node* rightMost = _node->_left;
while (rightMost->_right)
{
rightMost = rightMost->_right;
}
_node = rightMost;
}
else
{
// 孩子是父亲右的那个祖先
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_left)
{
cur = parent;
parent = cur->_parent;
}
_node = parent;
}
return *this;
}
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
bool operator!= (const Self& s)
{
return _node != s._node;
}
bool operator== (const Self& s)
{
return _node == s._node;
}
};
迭代器++操作和迭代器–操作
根据搜索二叉树的性质,迭代器++操作找下一个节点实质上就是走左、根、右
的顺序,迭代器- -则是反过来操作右、根、左
的顺序。当在end()
进行- -操作时要进行特殊处理
代码实现:
Self& operator++()
{
if (_node->_right)
{
// 右不为空,右子树最左节点就是中序第一个
Node* leftMost = _node->_right;
while (leftMost->_left)
{
leftMost = leftMost->_left;
}
_node = leftMost;
}
else
{
// 孩子是父亲左的那个祖先
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_right)
{
cur = parent;
parent = cur->_parent;
}
_node = parent;
}
return *this;
}
Self& operator--()
{
if (_node == nullptr) // end()
{
// --end(),特殊处理,走到中序最后一个节点,整棵树的最右节点
Node* rightMost = _root;
while (rightMost && rightMost->_right)
{
rightMost = rightMost->_right;
}
_node = rightMost;
}
else if (_node->_left)
{
// 左子树不为空,中序左子树最后一个
Node* rightMost = _node->_left;
while (rightMost->_right)
{
rightMost = rightMost->_right;
}
_node = rightMost;
}
else
{
// 孩子是父亲右的那个祖先
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_left)
{
cur = parent;
parent = cur->_parent;
}
_node = parent;
}
return *this;
}
在RBTree
类中的代码:
typedef RBTreeIterator<T, T&, T*> Iterator;
typedef RBTreeIterator<const T, const T&, const T*> Const_Iterator;
Node* Leftmost()
{
Node* cur = _root;
while (cur && cur->_left != nullptr)
{
cur = cur->_left;
}
return cur;
}
Iterator Begin()
{
Node* cur = Leftmost();
return Iterator(cur,_root);
}
Iterator End()
{
return Iterator(nullptr,_root);
}
Const_Iterator Begin() const
{
Node* cur = Leftmost();
return Iterator(cur, _root);
}
Const_Iterator End() const
{
return Const_Iterator(nullptr,_root);
}
红黑树是否满足规则验证
直接看代码:
void testrbtree1()
{
class A
{
public:
int operator()(const T& val)
{
return val;
}
};
RBTree<int, int, A> t;
//int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
for (auto e : a)
{
/*if (e == 9)
{
int i = 0;
}*/
t.Insert(e);
//cout << e << "->" << t.isbalancetree() << endl;
}
t.inorder();
cout << t.isbalance() << endl;
}
bool isbalance()
{
if (_root == nullptr)
return true;
if (_root->_col == RED)
{
return false;
}
// 参考值
int refNum = 0;
Node* cur = _root;
while (cur)
{
if (cur->_col == BLACK)
{
++refNum;
}
cur = cur->_left;
}
return Check(_root, 0, refNum);
}
void inorder()
{
_InOrder(_root);
cout << endl;
}
int Height()
{
return _Height(_root);
}
int Size()
{
return _Size(_root);
}
private:
bool Check(Node* root, int blackNum, const int refNum)
{
if (root == nullptr)
{
//cout << blackNum << endl;
if (refNum != blackNum)
{
cout << "存在黑色节点的数量不相等的路径" << endl;
return false;
}
return true;
}
if (root->_col == RED && root->_parent->_col == RED)
{
cout << root->_data << "存在连续的红色节点" << endl;
return false;
}
if (root->_col == BLACK)
{
blackNum++;
}
return Check(root->_left, blackNum, refNum)
&& Check(root->_right, blackNum, refNum);
}
int _Size(Node* root)
{
return root == nullptr ? 0 : _Size(root->_left) + _Size(root->_right) + 1;
}
int _Height(Node* root)
{
if (root == nullptr)
return 0;
int leftHeight = _Height(root->_left);
int rightHeight = _Height(root->_right);
return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_data << " ";
_InOrder(root->_right);
}
private:
int _rotateNum;
}
注意:要在左旋和右旋的实现代码中增加:
_rotateNum++;
在主函数中调用:
class A
{
public:
int operator()(const int& val)
{
return val;
}
};
int main()
{
RBTree<int, int,A> t;
t.testrbtree1();
return 0;
}
你学会了吗?
好啦,本章对于《C++:红黑树(RBT)》的学习就先到这里,如果有什么问题,还请指教指教,希望本篇文章能够对你有所帮助,我们下一篇见!!!
如你喜欢,点点赞就是对我的支持,感谢感谢!!!