红黑树性质
之前的博客中介绍了AVL树的性质与基本实现,红黑树作为另一种树型结构,他又有哪些性质呢?
红黑树作为一种二叉搜索树,与AVL树通过高度差来控制平衡不同,他在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。
因此,红黑树为了控制高度平衡,它具有如下的性质:
- 每个结点不是红色就是黑色
- 根节点是黑色的
- 如果一个节点是红色的,则它的两个孩子结点是黑色的
- 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点
- 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)
红黑树有如上性质就意味着他的结点控制有如下性质:
- 假如红黑树任意一条路径中黑色结点的数量为X,那么该红黑树的高度h则为2X>=h>=X
- 假如红黑树任意一条路径中黑色结点的数量为X,那么该红黑树的结点数量为2^X-1<=N<=2^2X-1
因此与严格平衡的AVL树搜索的时间复杂度相比,AVL树查找的时间复杂度为log2N,而红黑树查找一个树的时间复杂度为2*log2N,但是AVL树控制平衡是需要经常旋转来控制平衡,而红黑树控制平衡所要进行的旋转性能损耗会比AVL树低。
红黑树的简单实现
红黑树的基本构成
红黑树顾名思义每个节点中都存在一个变量来表示该节点的颜色(红色或者黑色),通过颜色以及红黑树的原则来控制红黑树的平衡,因此红黑树节点可以如下构造:
这里使用枚举来标识红黑树每个节点的颜色,这里默认红黑树每个节点存放的是一个pair类型的变量,红黑树也是通过一个三叉链来实现的。
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:
RBTree()
:_root(nullptr)
{
}
bool Insert(const pair<K, V>& kv);
void _InOrder(Node* root);
bool IsBalance();
private:
Node* _root;
};
红黑树的插入
红黑树插入和AVL树类似,先找到插入位置进行插入,然后再调整平衡,两者之间主要是调整平衡的方式有区别。下面先看一下红黑树的插入过程的代码实现
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 (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节点中的值比kv值小的时候,cur为该节点的右节点
{
parent->_right = cur;
cur->_parent = parent;
}
else//当parent节点中的值比kv值大的时候,cur为该节点的左节点
{
parent->_left = cur;
cur->_parent = parent;
}
}
通过如上的代码可以实现红黑树新增节点的插入,但是插入后可能会导致红黑树的不平衡或者违反红黑树的基本原则,因此需要对红黑树插入后进行平衡调节。这里平衡调节大致可以分为如下4种情况:
情况1
如下图所示,当parent节点为红,parent的兄弟节点也为红,grandparent节点为黑的时候,插入一个红色的左节点,此时出现了两个连续的红色节点,因此需要进行调整,这里的调整方法是将parent节点和uncle节点都改为黑色,并将grandparent节点改为红色,如果grandparent节点为根节点则改为黑色,否则就继续向上调整,代码实现如下:
while (parent && parent->_col == RED)//当parent存在且为红色时需要进行调整
{
Node* grandparent = parent->_parent;
if (parent == grandparent->_left)
{
//变色继续向上调整
Node* uncle = grandparent->_right;
//uncle存在且为红
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandparent->_col = RED;
cur = grandparent;
}
}
}
_root->_col=BLACK;
情况2
当cur为红色节点,parent也为红色节点,grandparent为黑色节点,此时uncle可能不存在也可能为黑色节点,此时如果uncle节点不存在,则cur一定是新插入的节点,因为parent是红色节点,如果cur不是新插入的节点则会不满足每条路径黑色节点个数相同这个原则。
如果uncle节点存在并且为黑的情况下,那么cur节点原来一定是黑色的,现在看到的为红色是因为cur的子树在向上调整的过程中变为红色的。
如果插入节点为左节点,则进行上面的一次右旋即可,如果插入的为右节点,那么则需要先进行左旋转化为前一种情况再进行右旋。
调整实现代码如下:
//uncle不存在或者存在且为黑色
if (cur == parent->_left)//cur是parent的左节点
{
RotateR(grandparent);
parent->_col = BLACK;
grandparent->_col = RED;
parent = cur->_parent;
}
else//如果当前节点是parent的右节点
{
RotateL(parent);
RotateR(grandparent);
cur->_col = BLACK;
grandparent->_col = RED;
}
break;
情况3
当parent为grandparent的右节点的时候,和上面两种情况类似,只需要进行相同的处理即可
代码如下:
Node* uncle = grandparent->_left;
if (uncle && uncle->_col == RED)
{
//变色向上处理
parent->_col = uncle->_col = BLACK;
grandparent->_col = RED;
cur = grandparent;
parent = cur->_parent;
}
else//uncle不存在或为黑色
{
if (cur == parent->_right)
{
RotateL(grandparent);
parent->_col = BLACK;
grandparent->_col = RED;
}
else
{
RotateR(parent);
RotateL(grandparent);
cur->_col = BLACK;
grandparent->_col = RED;
}
break;
}
旋转
这里的旋转和AVL树中的旋转方式类似,代码如下:
void RotateR(Node* parent)//右单旋
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR)
{
subLR->_parent = parent;
}
subL->_right = parent;
Node* parentParent = parent->_parent;
parent->_parent = subL;
if (parent == _root)
{
_root = subL;
_root->_parent = nullptr;
}
else
{
if (parentParent->_left == parent)
{
parentParent->_left = subL;
}
else
{
parentParent->_right = subL;
}
subL->_parent = parentParent;
}
}
void RotateL(Node* parent)//左单旋
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL)
{
subRL->_parent = parent;
}
subR->_left = parent;
Node* parentParent = parent->_parent;
parent->_parent = subR;
if (parent == _root)
{
_root = subR;
_root->_parent = nullptr;
}
else
{
if (parentParent->_left == parent)
{
parentParent->_left = subR;
}
else
{
parentParent->_right = subR;
}
subR->_parent = parentParent;
}
}
总结
红黑树的总体插入代码如下:
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 (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节点中的值比kv值小的时候,cur为该节点的右节点
{
parent->_right = cur;
cur->_parent = parent;
}
else//当parent节点中的值比kv值大的时候,cur为该节点的左节点
{
parent->_left = cur;
cur->_parent = parent;
}
while (parent && parent->_col == RED)//当parent存在且为红色时需要进行调整
{
Node* grandparent = parent->_parent;
if (parent == grandparent->_left)
{
Node* uncle = grandparent->_right;
//uncle存在且为红
if (uncle && uncle->_col == RED)
{
//变色继续向上调整
parent->_col = uncle->_col = BLACK;
grandparent->_col = RED;
cur = grandparent;
parent = cur->_parent;
}
//uncle不存在或者存在且为黑色
else
{
if (cur == parent->_left)//cur是parent的左节点
{
RotateR(grandparent);
parent->_col = BLACK;
grandparent->_col = RED;
}
else//如果当前节点是parent的右节点
{
RotateL(parent);
RotateR(grandparent);
cur->_col = BLACK;
grandparent->_col = RED;
}
break;
}
}
else//parent==grandparent->_right
{
Node* uncle = grandparent->_left;
if (uncle && uncle->_col == RED)
{
//变色向上处理
parent->_col = uncle->_col = BLACK;
grandparent->_col = RED;
cur = grandparent;
parent = cur->_parent;
}
else//uncle不存在或为黑色
{
if (cur == parent->_right)
{
RotateL(grandparent);
parent->_col = BLACK;
grandparent->_col = RED;
}
else
{
RotateR(parent);
RotateL(grandparent);
cur->_col = BLACK;
grandparent->_col = RED;
}
break;
}
}
}
_root->_col = BLACK;
return false;
}
红黑树的查找
红黑树的查找功能和其他搜索二叉树类似,使用中序遍历即可,代码实现如下:
void InOrder()
{
return _InOrder(_root);
}
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_kv.first << ":" << root->_kv.second << endl;
_InOrder(root->_right);
}
红黑树平衡判断
红黑树的平衡判断主要从红黑树的原则入手:第一则是每条路径中黑色节点的个数相同,第二则是不会出现连续的红色节点,第三则是根节点必须为黑色,判断平衡的函数依据以上几个原则来实现:
bool IsBalance()//判断二叉树是否平衡
{
if (_root && _root->_col == RED)
{
cout << "根节点不是黑色" << endl;
return false;
}
int banchmark = 0;//统计某条路径中黑色节点的个数
Node* left = _root;//这里选取最左节点路径
while (left)
{
if (left->_col == BLACK)
{
++banchmark;
}
left = left->_left;
}
int blacknum = 0;
return _IsBalance(_root, banchmark, blacknum);
}
bool _IsBalance(Node* root, int banchmark, int blacknum)
{
if (root == nullptr)
{
if (banchmark != blacknum)
{
cout << "存在路径黑色节点不相等" << endl;
return false;
}
return true;
}
if (root->_col == RED && root->_parent->_col == RED)
{
cout << "出现连续红色节点" << endl;
return false;
}
if (root->_col == BLACK)
{
blacknum++;
}
return _IsBalance(root->_left, banchmark, blacknum) && _IsBalance(root->_right, banchmark, blacknum);
}
红黑树的高度计算
这里红黑树的高度计算和正常二叉树的高度计算一样,找出最长路径即可
int Height()
{
return _Height(_root);
}
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;
}
红黑树的测试
#include<iostream>
using namespace std;
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:
RBTree()
:_root(nullptr)
{
}
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 (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节点中的值比kv值小的时候,cur为该节点的右节点
{
parent->_right = cur;
cur->_parent = parent;
}
else//当parent节点中的值比kv值大的时候,cur为该节点的左节点
{
parent->_left = cur;
cur->_parent = parent;
}
while (parent && parent->_col == RED)//当parent存在且为红色时需要进行调整
{
Node* grandparent = parent->_parent;
if (parent == grandparent->_left)
{
Node* uncle = grandparent->_right;
//uncle存在且为红
if (uncle && uncle->_col == RED)
{
//变色继续向上调整
parent->_col = uncle->_col = BLACK;
grandparent->_col = RED;
cur = grandparent;
parent = cur->_parent;
}
//uncle不存在或者存在且为黑色
else
{
if (cur == parent->_left)//cur是parent的左节点
{
RotateR(grandparent);
parent->_col = BLACK;
grandparent->_col = RED;
}
else//如果当前节点是parent的右节点
{
RotateL(parent);
RotateR(grandparent);
cur->_col = BLACK;
grandparent->_col = RED;
}
break;
}
}
else//parent==grandparent->_right
{
Node* uncle = grandparent->_left;
if (uncle && uncle->_col == RED)
{
//变色向上处理
parent->_col = uncle->_col = BLACK;
grandparent->_col = RED;
cur = grandparent;
parent = cur->_parent;
}
else//uncle不存在或为黑色
{
if (cur == parent->_right)
{
RotateL(grandparent);
parent->_col = BLACK;
grandparent->_col = RED;
}
else
{
RotateR(parent);
RotateL(grandparent);
cur->_col = BLACK;
grandparent->_col = RED;
}
break;
}
}
}
_root->_col = BLACK;
return false;
}
void InOrder()
{
return _InOrder(_root);
}
bool IsBalance()//判断二叉树是否平衡
{
if (_root && _root->_col == RED)
{
cout << "根节点不是黑色" << endl;
return false;
}
int banchmark = 0;//统计某条路径中黑色节点的个数
Node* left = _root;//这里选取最左节点路径
while (left)
{
if (left->_col == BLACK)
{
++banchmark;
}
left = left->_left;
}
int blacknum = 0;
return _IsBalance(_root, banchmark, blacknum);
}
int Height()
{
return _Height(_root);
}
private:
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_kv.first << ":" << root->_kv.second << endl;
_InOrder(root->_right);
}
bool _IsBalance(Node* root, int banchmark, int blacknum)
{
if (root == nullptr)
{
if (banchmark != blacknum)
{
cout << "存在路径黑色节点不相等" << endl;
return false;
}
return true;
}
if (root->_col == RED && root->_parent->_col == RED)
{
cout << "出现连续红色节点" << endl;
return false;
}
if (root->_col == BLACK)
{
blacknum++;
}
return _IsBalance(root->_left, banchmark, blacknum) && _IsBalance(root->_right, banchmark, blacknum);
}
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 RotateR(Node* parent)//右单旋
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR)
{
subLR->_parent = parent;
}
subL->_right = parent;
Node* parentParent = parent->_parent;
parent->_parent = subL;
if (parent == _root)
{
_root = subL;
_root->_parent = nullptr;
}
else
{
if (parentParent->_left == parent)
{
parentParent->_left = subL;
}
else
{
parentParent->_right = subL;
}
subL->_parent = parentParent;
}
}
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL)
{
subRL->_parent = parent;
}
subR->_left = parent;
Node* parentParent = parent->_parent;
parent->_parent = subR;
if (parent == _root)
{
_root = subR;
_root->_parent = nullptr;
}
else
{
if (parentParent->_left == parent)
{
parentParent->_left = subR;
}
else
{
parentParent->_right = subR;
}
subR->_parent = parentParent;
}
}
private:
Node* _root;
};
上面是该红黑树简单实现的完整代码,接下来使用插入一段随机的数组来验证该红黑树的构建,测试代码如下:
void TestRBTree()
{
int arr[] = { 2,15,12,74,64,13,15,10,11,13,12,18 };
RBTree<int, int> Node;
for (auto e : arr)
{
Node.Insert(make_pair(e, e));
}
Node.InOrder();
cout << Node.IsBalance() << endl;
cout << Node.Height() << endl;
}
void TestRBTree()
{
vector<int> v;
//int arr[] = { 2,15,12,74,64,13,15,10,11,13,12,18 };
srand(time(0));
int N = 1000000;
for (int i = 0; i < N; i++)
{
v.push_back(rand());
}
RBTree<int, int> Node;
for (auto e : v)
{
Node.Insert(make_pair(e, e));
}
//Node.InOrder();
cout << Node.IsBalance() << endl;
cout << Node.Height() << endl;
}
这里测试1000000个随机数进行插入,生成二叉树,可以看到他只有19层左右