目录
(4)父节点为红,叔叔不存在或存在且为黑,新插入的节点在父节点的左侧
(5)父节点为红,叔叔不存在或存在且为黑,新插入的节点在父节点的右侧
一、什么是红黑树
红黑树的表意就是一颗每个节点带有颜色的二叉搜索树,并通过对节点颜色的控制,使该二叉搜索树达到尽量平衡的状态。所谓尽量平衡的状态就是:红黑树确保没有一条路径比其他路径长两倍。和AVL树不同的是,AVL树是一棵平衡树,而红黑树可能平衡也可能不平衡(因为是尽量平衡的状态)
二、红黑树的约定
要实现一棵红黑树,即要红黑树确保没有一条路径比其他路径长两倍。通过对节点颜色的约定来实现这一目标。
1.根节点是黑色的
2.如果一个节点是红色的,则它的两个孩子都是黑色的
3.对于每个节点,从该节点到其他后代节点的简单路径上,均包含相同的黑色节点。
实现了这三条颜色规则的二叉搜索树,即也实现了没有一条路径比其他路径长两倍,即实现了一棵红黑树。
注意性质三所说的叶子节点是空节点,空节点都看成是黑色节点。
三、红黑树vsAVL
1.调整平衡的实现机制不同
红黑树根据节点颜色(同一父节点出发到叶子节点,所有路径上的黑色节点数目一样),一些约定和旋转实现。
AVL根据树的平衡因子(所有节点的左右子树高度差的绝对值不超过1)和旋转决定。
2.红黑树的插入效率更高
红黑树是用非严格的平衡来换取增删节点时候旋转次数的降低,任何不平衡都会在三次旋转之内解决,红黑树并不追求“完全平衡”,它只要求部分地达到平衡要求,降低了对旋转的要求,从而提高了性能
而AVL是严格平衡树(高度平衡的二叉搜索树),因此在增加或者删除节点的时候,根据不同情况,旋转的次数比红黑树要多。所以红黑树的插入效率更高
3.AVL查找效率高
如果你的应用中,查询的次数远远大于插入和删除,那么选择AVL树,如果查询和插入删除次数几乎差不多,应选择红黑树。即,有时仅为了排序(建立-遍历-删除),不查找或查找次数很少,R-B树合算一些。
四、红黑树的实现
实现一棵红黑树,本质是实现它的插入函数,使插入函数可以实现红黑树的颜色约定,它的实现分为两步,即先找到插入的位置,再控制平衡。插入函数返回值设计为bool,插入成功返回true,失败返回false。控制平衡时,需要关注四个节点,即新插入的节点,它的父节点,它的叔叔节点,它的祖父节点。
1.找到插入的位置
当为第一个节点的时候,颜色设为黑,直接插入。
当插入的不是第一个节点时,颜色设为红,需要根据二叉搜索树的性质找到位置。并实现三叉链。
if (_root == nullptr)
{
_root = new Node(kv);
_root->_col = Black;
return true;
}
Node* parent = nullptr;
Node* cur = _root;
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);
cur->_col= Red;
if (parent->_kv.first < kv.first)
{
parent->_right = cur;
cur->_parent = parent;
}
else
{
parent->_left = cur;
cur->_parent = parent;
}
2.控制平衡
(1)当父节点为黑
当父节点为黑的时候,由于插入的子节点的颜色为红,对三个约定没有任何影响,因此不需要调整平衡。
(2)判断父节点再祖父节点的位置
通过判断父节点在祖父节点的位置,来定义叔叔节点的位置,以及之后的旋转方向的判断。
while (parent&& parent->_col == Red)
{
Node* grandfather = parent->_parent;
if (parent == grandfather->_right)
{
Node* uncle = grandfather->_right;
//
}
else
{
Node* uncle = grandfather->_left;
//
}
}
首先需要使用大循环,这个循环是为情况1而准备的,情况2和3直接跳出循环即可,因为只有情况1对上层循环有影响。
下面我们以父节点在祖父节点的左侧为例,右侧同理。
(3)叔叔节点存在且为红
解决方案:将父节点和叔叔节点设为黑,将祖父节点设为红。然后将祖父节点作为新节点继续向上平衡。如果祖父节点是根节点,那么需要再将其置为黑。
注意:这种情况和其它情况不同的是,需要将祖父节点作为新插入的节点继续向上遍历,这说明需要一个循环。而其他类型的情况直接break跳出这个循环即可。
if (uncle&& uncle->_col == Red)
{
parent->_col = uncle->_col = Black;
grandfather->_col = Red;
cur = grandfather;
parent = cur->_parent;
}
这种情况只需要控制颜色即可,但是要继续向上循环。
(4)父节点为红,叔叔不存在或存在且为黑,新插入的节点在父节点的左侧
解决方案:对祖父节点右旋操作,并将祖父节点置为红,父节点置为黑。
if (cur == parent->_left)
{
RotateR(grandfather);
parent->_col = Black;
grandfather->_col = Red;
}
(5)父节点为红,叔叔不存在或存在且为黑,新插入的节点在父节点的右侧
解决方案:进行双旋,即对父节点进行左单选,祖父节点进行右单旋。将子节点置为黑,将祖父系欸点置为红。
else
{
RotateL(parent);
RotateR(grandfather);
cur->_col = Black;
grandfather->_col = Red;
}
(6)最后置黑
每一次插入都对根节点置为黑操作,因为第一种情况可能导致根节点不是黑。同时对根节点置黑也并不违反三条规定。
3.测试代码
当我们处理完父节点在祖父节点的左侧后,处理父节点在祖父节点的右侧。
全部处理之后,我们的插入代码就完成了,接下来要对整个树进行测试,即对三个约定来进行测试:
1.当根节点为红时,返回false
2.判断一个节点和其父节点的颜色否都为红,若都为红返回false。
3.以最左的一条路径上的根节点数量为基准,通过递归遍历每一条路径上的黑色节点的数量,当每条路径遍历节点到空时,将两者进行比较,如果最终结果不相等则返回false。
bool IsBalance()
{
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);
}
bool Check(Node* root, int blackNum, const int refNum)
{
if (root == nullptr)
{
if (refNum != blackNum)
{
cout << "存在黑色节点数量不相等的路径" << endl;
return false;
}
return true;
}
if (root->_col == RED && root->_parent->_col == RED)
{
cout << root->_kv.first << "存在连续红色节点" << endl;
return false;
}
if (root->_col == Black)
{
blackNum++;
}
return Check(root->_left, blackNum, refNum)
&& Check(root->_right, blackNum, refNum);
}
五 代码
#pragma once
#include <iostream>
#include <assert.h>
using namespace std;
enum Color
{
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;
Color _col;
RBTreeNode(const pair<K,V>& kv)
:_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_col(Red)
,_kv(kv)
{}
};
template <class K,class V>
class RBTree
{
typedef RBTreeNode<K, V> Node;
public:
void RotateR(Node* parent)
{
Node* sub = parent->_left;
Node* subR = sub->_right;
parent->_left = subR;
if (subR)
subR->_parent = parent;
if (parent == _root)
{
_root = sub;
}
else
{
Node* ppNode = parent->_parent;
if (ppNode->_left == parent)
{
ppNode->_left = sub;
}
else
{
ppNode->_right = sub;
}
sub->_parent = ppNode;
}
sub->_right = parent;
parent->_parent = sub;
}
void RotateL(Node* parent)
{
Node* sub = parent->_right;
Node* subL = sub->_left;
parent->_right = subL;
if (subL)
subL->_parent = parent;
if (parent == _root)
{
_root = sub;
}
else
{
Node* ppNode = parent->_parent;
if (ppNode->_left = parent)
{
ppNode->_left = sub;
}
else
{
ppNode->_right = sub;
}
sub->_parent = parent;
}
sub->_left = parent;
parent->_parent = sub;
}
bool Insert(const pair<K, V>& kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
_root->_col = Black;
return true;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (kv.first < cur->_kv.first)
{
parent = cur;
cur = cur->_left;
}
else if (kv.first > cur->_kv.first)
{
parent = cur;
cur = cur->_right;
}
else
{
return false;
}
}
cur = new Node(kv);
cur->_col = Red;
if (kv.first < parent->_kv.first)
{
parent->_left = cur;
cur->_parent = parent;
}
while (parent&& parent->_col == Red)
{
Node* grandfather = parent->_parent;
if (parent == grandfather->_left)
{
Node* uncle = grandfather->_right;
if (uncle&& uncle->_col == Red)
{
parent->_col = uncle->_col = Black;
grandfather->_col = Red;
cur = grandfather;
parent = cur->_parent;
}
else
{
if (cur == parent->_left)
{
RotateR(grandfather);
parent->_col = Black;
grandfather->_col = Red;
}
else
{
RotateL(parent);
RotateR(grandfather);
cur->_col = Black;
grandfather->_col = Red;
}
break;
}
_root->_col = Black;
}
else
{
Node* uncle = grandfather->_left;
if (uncle && uncle->_col == Red)
{
parent->_col = uncle->_col = Black;
grandfather->_col = Red;
cur = grandfather;
parent = cur->_parent;
}
else
{
if (cur == parent->_right)
{
RotateL(grandfather);
parent->_col = Black;
grandfather->_col = Red;
}
else
{
RotateR(parent);
RotateL(grandfather);
cur->_col = Black;
grandfather->_col = Red;
}
break;
}
_root->_col = Black;
}
}
}
bool IsBalance()
{
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);
}
bool Check(Node* root, int blackNum, const int refNum)
{
if (root == nullptr)
{
if (refNum != blackNum)
{
cout << "存在黑色节点数量不相等的路径" << endl;
return false;
}
return true;
}
if (root->_col == Red && root->_parent->_col == Red)
{
cout << root->_kv.first << "存在连续红色节点" << endl;
return false;
}
if (root->_col == Black)
{
blackNum++;
}
return Check(root->_left, blackNum, refNum)
&& Check(root->_right, blackNum, refNum);
}
private:
Node* _root = nullptr;
};