目录
一、概念
红黑树是对平衡二叉树的改进。平衡二叉树追求极致的平衡,因此在插入时需要大量频繁的旋转达到平衡的目。红黑树则放宽了对平衡的要求,减少了大量的旋转,虽然严格意义上红黑树的查找效率不如二叉平衡树,二叉平衡树的查找效率比红黑树高常数倍。但对于CPU来说,这点查找差距实际上没有什么影响,而红黑树插入减少了大量的旋转反而综合性能优于平衡二叉树。
红黑树的性质:
1、 每个结点不是红色就是黑色 ,但根节点必须是黑色;
2、如果一个节点是红色的,则它的两个孩子结点是黑色的 ,也就是不存在连续的红色节点;
3、 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点;
4、 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)。
最优情况:全黑或每条路径都是一黑一红的满二叉树,高度logN
最差情况:每颗子树左子树全黑,右子树一黑一红。高度2*logN。
二、红黑树的插入
以下是本文红黑树节点的数据结构:
enum Color
{
red,black
};
//红黑树的节点数据结构
template<class K, class V>
struct RBTreeNode
{
pair<K, V> _data;
RBTreeNode<K, V>* _left;
RBTreeNode<K, V>* _right;
RBTreeNode<K, V>* _parent;
Color _color;
RBTreeNode(pair<K, V> data)
:_data(data)
,_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_color(red)
{}
};
由上述代码我们可以得知新建一个节点的颜色默认是红色,如果我们新建节点颜色为黑色,无论我们插入哪里,都会违反性质3,因此我们默认插入节点颜色为红色。
(一)插入步骤
红黑树的插入与二叉平衡树类似,只是依靠不同的方式来保证平衡。首先按照二叉排序树的特性寻找插入节点的目标位置。插入节点后再根据其父节点以及叔叔节点的颜色进行不同的操作。
旋转部分详见:【数据结构】平衡二叉树-CSDN博客
(二)插入的三种情况
以下三种情况都是插入后出现了连续的红色节点所以需要特殊处理,若插入节点后其父节点为黑色,则无需进行任何特殊处理。以下三种情况都是特殊处理。方便
1、叔叔存在且为红色
插入节点后出现连续的红色节点且叔叔节点颜色也为红色,只需将父亲节点和叔叔节点的颜色染黑,将祖父节点的颜色染红即可。经过该操作以后该子树到其下任意一条路径上的黑色节点数目不变。
需要注意的是,因为该子树的根被染红,因此进行该操作后需要 cur 指向 grandparent,继续检查上层节点是否出现连续的红色,也就是祖父节点的父节点也有可能是红色。所以情况一不仅仅是插入节点后可能出现的情况,也有可能是向上调整的过程中可能出现的情况。
2、叔叔不存在/存在且为黑色(单旋)
这里我们假定叔叔存在且为黑色(实际叔叔存不存在都不影响操作),首先需要说明的是情况二只有可能出现在情况一向上调整的过程中,不可能出现在刚插入节点的过程中。若刚插入就满足情况二,说明在插入前该树就已经不满足红黑树的性质了。
针对本情况,首先对父节点进行右旋,同时将父节点染黑并将祖父节点染红。经过此操作之后,单论黑色节点的数量的话,该子树到任意路径上黑色节点的数量与旋转前保持一致并且旋转后没有出现连续的红色节点。仍保持红黑树的特性。
以上其实只是连续红色节点出现在左子树上,若连续红色节点出现在右子树时与上述操作类似,进行左旋并对父节点和祖父节点进行染色即可,这里便不进行赘述了。经过以上操作后子树的根节点为黑色,因此无需向上继续检查平衡因子异常了。
3、叔叔不存在/存在且为黑色(双旋)
这里我们假定叔叔存在且为黑色(实际叔叔存不存在都不影响操作),与情况二类似,情况三只有可能出现在情况一向上调整的过程中,不可能出现在刚插入节点的过程中。若刚插入就满足情况三,说明在插入前该树就已经不满足红黑树的性质了。
针对本情况,首先对父节点进行左旋,再对祖父节点进行右旋,并将祖父节点染红和插入节点染黑。经过此操作之后,单论黑色节点的数量的话,该子树到任意路径上黑色节点的数量与旋转前保持一致并且旋转后没有出现连续的红色节点。仍保持红黑树的特性。
以上其实只是连续红色节点出现在左子树上,若连续红色节点出现在右子树时与上述操作类似,进行右旋和左旋并对祖父节点和插入节点进行染色即可,这里便不进行赘述了。经过以上操作后子树的根节点为黑色,因此无需向上继续检查平衡因子异常了。
(三)插入代码
总结下三种情况,实际只有第一种情况执行后需要向上检查平衡因子异常,第二种第三种情况执行后无需向上检查平衡因子异常。
bool Insert(const pair<K, V>& data)
{
//如果根节点为空直接插入即可
if (_root == nullptr)
{
_root = new Node(data);
_root->_color = black;
return true;
}
Node* parent = nullptr;
Node* cur = _root;
//寻找插入节点目标位置
while (cur)
{
if (cur->_data.first < data.first)
{
parent = cur;
cur = cur->_right;
}
else if(cur->_data.first > data.first)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
//插入节点
cur = new Node(data);
if (parent->_data.first < data.first)
parent->_right = cur;
else
parent->_left = cur;
cur->_parent = parent;
//处理出现连续红色节点
while (parent && parent->_color == red)
{
Node* grandparent = parent->_parent;
Node* uncle = nullptr;
if (grandparent == nullptr)
break;
if (parent == grandparent->_left)
uncle = grandparent->_right;
else
uncle = grandparent->_left;
//情况一
if (uncle && uncle->_color == red)
{
parent->_color = uncle->_color = black;
grandparent->_color = red;
cur = grandparent;
parent = cur->_parent;
}
else
{
//情况二
if (grandparent->_right == parent && parent->_right == cur)
{
RotateL(grandparent);
parent->_color = black;
grandparent->_color = red;
}
else if (grandparent->_left == parent && parent->_left == cur)
{
RotateR(grandparent);
parent->_color = black;
grandparent->_color = red;
}
//情况三
else if (grandparent->_right == parent && parent->_left == cur)
{
RotateR(parent);
RotateL(grandparent);
cur->_color = black;
grandparent->_color = red;
}
else if (grandparent->_left == parent && parent->_right == cur)
{
RotateL(parent);
RotateR(grandparent);
cur->_color = black;
grandparent->_color = red;
}
else
assert(false);
break;
}
//无论如何根节点必须染黑
_root->_color = black;
}
return true;
}
三、红黑树的平衡检测
相对于平衡二叉树的严格检查平衡,红黑树的检查要宽松点。首先要检查根节点颜色是否为黑色,再检查是否存在连续的红色节点,最后再检查各个路径的黑色节点数目是否一致。
//判断是否为红黑树
bool isRBTree()
{
//检查根节点
if (_root->_color == red)
return false;
Node* cur = _root;
int ref = 0;
//获取最左路径下的黑色节点用于参考
while (cur)
{
if (cur->_color == black)
++ref;
cur = cur->_left;
}
return check(_root, 0, ref);
}
bool check(Node* t, int num, int ref)
{
if (t == nullptr)
{
//判断各个路径的黑色节点数目是否相同
if (num != ref)
{
cout << "黑色节点数目异常" << endl;
return false;
}
return true;
}
//检查是否出现了连续的红色节点
if (t->_color == red && t->_parent->_color == red)
{
cout << "出现连续红色节点" << endl;
return false;
}
if (t->_color == black)
num++;
return check(t->_left, num, ref) && check(t->_right, num, ref);
}
四、整体代码
#pragma once
#include <iostream>
#include <cassert>
using namespace std;
enum Color
{
red,black
};
template<class K, class V>
struct RBTreeNode
{
pair<K, V> _data;
RBTreeNode<K, V>* _left;
RBTreeNode<K, V>* _right;
RBTreeNode<K, V>* _parent;
Color _color;
RBTreeNode(pair<K, V> data)
:_data(data)
,_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_color(red)
{}
};
template<class K, class V>
class RBTree
{
public:
typedef RBTreeNode<K, V> Node;
RBTree()
:_root(nullptr)
{}//中序遍历
void Inorder()
{
_Inorder(_root);
}
// 左单旋
void RotateL(Node* pParent)
{
Node* parent = pParent;
Node* pNode = parent->_parent;
Node* subR = parent->_right;
parent->_right = subR->_left;
if (subR->_left)
subR->_left->_parent = parent;
subR->_left = parent;
parent->_parent = subR;
if (pNode == nullptr)
{
_root = subR;
subR->_parent = nullptr;
}
else
{
if (pNode->_left == parent)
{
pNode->_left = subR;
}
else
{
pNode->_right = subR;
}
subR->_parent = pNode;
}
}
// 右单旋
void RotateR(Node* pParent)
{
Node* parent = pParent;
Node* pNode = parent->_parent;
Node* subL = parent->_left;
parent->_left = subL->_right;
if (subL->_right)
subL->_right->_parent = parent;
subL->_right = parent;
parent->_parent = subL;
if (pNode == nullptr)
{
_root = subL;
subL->_parent = nullptr;
}
else
{
if (pNode->_left == parent)
{
pNode->_left = subL;
}
else
{
pNode->_right = subL;
}
subL->_parent = pNode;
}
}
//插入节点
bool Insert(const pair<K, V>& data)
{
if (_root == nullptr)
{
_root = new Node(data);
_root->_color = black;
return true;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_data.first < data.first)
{
parent = cur;
cur = cur->_right;
}
else if(cur->_data.first > data.first)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
cur = new Node(data);
if (parent->_data.first < data.first)
parent->_right = cur;
else
parent->_left = cur;
cur->_parent = parent;
while (parent && parent->_color == red)
{
Node* grandparent = parent->_parent;
Node* uncle = nullptr;
if (grandparent == nullptr)
break;
if (parent == grandparent->_left)
uncle = grandparent->_right;
else
uncle = grandparent->_left;
if (uncle && uncle->_color == red)
{
parent->_color = uncle->_color = black;
grandparent->_color = red;
cur = grandparent;
parent = cur->_parent;
}
else
{
if (grandparent->_right == parent && parent->_right == cur)
{
RotateL(grandparent);
parent->_color = black;
grandparent->_color = red;
}
else if (grandparent->_left == parent && parent->_left == cur)
{
RotateR(grandparent);
parent->_color = black;
grandparent->_color = red;
}
else if (grandparent->_right == parent && parent->_left == cur)
{
RotateR(parent);
RotateL(grandparent);
cur->_color = black;
grandparent->_color = red;
}
else if (grandparent->_left == parent && parent->_right == cur)
{
RotateL(parent);
RotateR(grandparent);
cur->_color = black;
grandparent->_color = red;
}
else
assert(false);
break;
}
_root->_color = black;
}
return true;
}
//判断是否为红黑树
bool isRBTree()
{
if (_root->_color == red)
return false;
Node* cur = _root;
int ref = 0;
while (cur)
{
if (cur->_color == black)
++ref;
cur = cur->_left;
}
return check(_root, 0, ref);
}
private:
bool check(Node* t, int num, int ref)
{
if (t == nullptr)
{
if (num != ref)
{
cout << "黑色节点数目异常" << endl;
return false;
}
return true;
}
if (t->_color == red && t->_parent->_color == red)
{
cout << "出现连续红色节点" << endl;
return false;
}
if (t->_color == black)
num++;
return check(t->_left, num, ref) && check(t->_right, num, ref);
}
//中序遍历
void _Inorder(Node* root)
{
if (root == nullptr)
return;
_Inorder(root->_left);
cout << root->_data.first << " ";
_Inorder(root->_right);
}
Node* _root;
};
标签:cur,parent,color,红黑树,grandparent,数据结构,节点,left
From: https://blog.csdn.net/Sweet_0115/article/details/144577423