C++红黑树
Posted 西科陈冠希
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++红黑树相关的知识,希望对你有一定的参考价值。
红黑树
红黑树概念
红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。
红黑树性质
- 每个结点不是红色就是黑色
- 根节点是黑色的
- 如果一个节点是红色的,则它的两个孩子结点是黑色的
- 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点
- 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)(NIL结点)
注意
如果一条支路全是黑节点,其肯定为最短路径,因为按照性质不能有两个红,也就是说黑红得搭配这来,如果一条直路没有红就为最短,最长路径就是一个黑配一个红也就是最短路径的二倍,所以其路径在【h,2h】之间,其复杂度最短为log2x-2log2x和AVL树相差不多。
红黑树结构
红黑树的实现中增加一个头结点,因为跟节点必须为黑色,为了与根节点进
行区分,将头结点给成黑色,并且让头结点的 pParent 域指向红黑树的根节点,pLeft域指向红黑树中最小的节点,_pRight域指向红黑树中最大的节点,如下:
红黑树插入(条件情况)
情况一
无论哪种情况其都得先找到插入位置因为红黑树底层就是搜索二叉树所以也是通过搜索二叉树找到插入位置,因为新节点的默认颜色是红色,因此:如果其双亲节点的颜色是黑色,没有违反红黑树任何性质,则不需要调整;但当新插入节点的双亲节点颜色为红色时,就违反了性质三不能有连在一起的红色节点,此时需要对红黑树分情况来讨论:
约定:cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点
这里插入的cur为红,p和u也为红,不能有俩红(插入的默认为红)这种情况就需要将p和u变黑,并且将g变红。
此时g为红就分为两种情况,如果g为根节点通过红黑树的规则就需要将根节点设置为黑,并结束,如果g不是根节点就需要依次向上迭代。cur变为g一步步迭代。直到不是两个红色连续或者进入其他情况,u为黑或者不存在
情况二
当插入的curp的左后,uncle节点不是红此时就需要进行旋转(如果还是和情况一样变色是没有办法去完成红黑树的规则的)
和AVL树的旋转类似,将p的右给到g的左,然后让g成为p的右。再将g变为红色p变为黑色
此时就会满足红黑树的性质条件。
情况三
当cur插入的是p的右边此时再进行单旋转是没有办法符合红黑树的条件的,就引入了双旋
先将p作为轴点进行左旋cur的左给p的右p成为cur的左。
此时就变成了一个情况二只需要继续对其进行右旋转即可,cur的右为g的左,g变成cur的右边。然后将cur的颜色变为黑色,g的颜色变为红色。
红黑树验证
怎么验证呢?答案就在谜面上(红黑树规则),红黑树是怎么形成的就怎么验证,
- 检测其是否满足二叉搜索树(中序遍历是否为有序序列)
- 检测其是否满足红黑树的性质
与AVL树的比较及其应用
红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O(log2N ),红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的数,所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。
** 应用:**
- C++ STL库 – map/set、mutil_map/mutil_set
- Java 库
- linux内核
- 其他一些库
红黑树模拟实现代码
enum Color
RED,
BLACK
;
template<class T>
struct RBTreeNode
RBTreeNode(const T& data = T(), Color color = RED)
: _pLeft(nullptr)
, _pRight(nullptr)
, _pParent(nullptr)
, _data(data)
, _color(color)
RBTreeNode<T>* _pLeft;
RBTreeNode<T>* _pRight;
RBTreeNode<T>* _pParent;
T _data;
Color _color;
;
template<class T>
class RBtree
typedef RBTreeNode<T> Node;
// 在红黑树中插入值为data的节点,插入成功返回true,否则返回false
// 注意:为了简单起见,本次实现红黑树不存储重复性元素
bool Insert(const T& data)
Node* _root = GetRoot();
if (_root == nullptr)
Node* _root = Node(data);
return true
else
Node* cur = _root;
Node* parent = nullptr;
while (cur)
parent = cur;
if (cur->_data > data)
cur = cur->_pLeft;
else
cur = cur->_pRight;
Node* New = new Node(data);
cur = New;
if (parent->_data > data)
parent->_pLeft = cur;
cur->_pParent = parent;
else
parent->_pRight = cur;
cur->_pParent = parent;
while (parent != _pHead && parent->_color == RED)
Node* grandfather = parent->_pParent;
if (grandfather->_pLeft == parent)
Node* uncle = grandfather->_pRight;
if (uncle && uncle->_color == RED)
uncle->_color == BLACK;
parent->_color == BLACK;
grandfather->_color == RED;
cur = grandfather;
parent = cur->_pParent;
else//uncle not exists black
if (cur = parent->_pLeft)
//右单旋
RotateR(grandfather);
parent->_color = BLACK;
grandfather->_color = RED;
else
//双旋转
RotateL(parent);
RotateR(grandfather);
cur->_color = BLACK;
grandfather->_color = RED;
//uncle left
else
if (cur == parent->_pRight)
RotateL(grandfather);
grandfather->_color = RED;
parent->_color = BLACK;
else
RotateR(parent);
RotateL(grandfather);
cur->_color = BLACK;
grandfather->_color = RED;
break;
_root->_color = BLACK;
return true;
// 检测红黑树中是否存在值为data的节点,存在返回该节点的地址,否则返回nullptr
Node* Find(const T& data)
Node* cur = _pHead->_pParent;
if (cur->_data == data)
return cur;
Find(cur->_pLeft);
Find(cur->_pRight);
return nullptr;
// 获取红黑树最左侧节点
Node* LeftMost()
Node* root = _pHead->_pParent;
Node* parent = nullptr;
while (root)
parent = root;
root = root->_pLeft;
return parent;
// 获取红黑树最右侧节点
Node* RightMost()
Node* root = _pHead->_pParent;
Node* parent = nullptr;
while (root)
parent = root;
root = root->_pRight;
return parent;
void _InOrder(Node* pRoot)
if (pRoot)
_InOrder(pRoot->_pLeft);
cout << pRoot->_data << " ";
_InOrder(pRoot->_pRight);
// 检测红黑树是否为有效的红黑树,注意:其内部主要依靠_IsValidRBTRee函数检测
bool IsValidRBTRee()
if (_pHead->_pParent && _pHead->_pParent->_color == RED)
return false;
int blacknum = 0;
Node* cur = _pHead->_pParent;
while (cur)
if (cur->_color == BLACK)
++blacknum;
cur->_pLeft;
int num = 0;
return _CheckRedCol(_root)
&& _CheckBlackNum(_root, num, blacknum);
private:
bool _IsValidRBTRee(Node* pRoot, size_t blackCount, size_t pathBlack)
if (pRoot == nullptr)
return blackCount==pathBlack;
if (pRoot->_color == BLACK)
blackCount++;
return _CheckBlackNum(root->_left, blackCount, pathBlack)
&& _CheckBlackNum(root->_right, blackCount, pathBlack);
// 左单旋
void RotateL(Node* pParent)
Node* subR = pParent->_pRight;
Node* subRL = subR->_pLeft;
pParent->_pRight = subR;
if (subRL)
subRL->_pParent = pParent;
subR->_left = pParent;
pParent->_pParent = subR;
Node* grandfather = pParent->_pParent;
if (pParent == _pHead->_pParent)
_pHead->_pParent = subR;
subR->_pParent = nullptr;
else
if (grandfather->_pLeft == pParent)
grandfather->_pLeft = subR;
subR->_pParent = grandfather;
else
grandfather->_pRight = subR;
subR->_pParent = grandfather;
// 右单旋
void RotateR(Node* pParent)
Node* subL = pParent->_pLeft;
Node* subLR = subL->_pRight;
pParent->_pLeft = subL;
if (subLR)
subL->_pParent = pParent;
subL->_pRight = pParent;
pParent->_pParent = subL;
Node* grandfather = pParent->_pParent;
subL->_pRight = pParent;
pParent->_pParent = subL;
if (pParent == _pHead->_pParent)
_pHead->_pParent=subL;
subL->_pParent = _pHead;
else
if (grandfather->_pLeft == pParent)
grandfather->_pLeft = subL;
subL->_pParent = grandfather;
else
grandfather->_pRight = subL;
subL->_pParent = grandfather;
// 为了操作树简单起见:获取根节点
Node*& GetRoot()
return _pHead->_pParent;
private:
Node* _pHead;
;
以上是关于C++红黑树的主要内容,如果未能解决你的问题,请参考以下文章