红黑树的概念
- 红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。
红黑树的性质
-
每个结点不是红色就是黑色
-
根节点是黑色的
-
如果一个节点是红色的,则它的两个孩子结点是黑色的
也就是说不存在两个红色节点相连 -
对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点俗称:黑高
从13的节点到任何NIL节点黑节点数都为3
- 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)不存在两个红色节点相连,也就是任意一个节点到每个叶子节点的路径包含了相同数量的黑节点
从性质4可以推出:如果一个节点存在黑子节点,那么这个节点肯定有两个子节点。
如果root没有右子节点就违反了性质4了,所以当一个节点的一个子节点为黑,另一个节点就不能为NIL。
红黑树能自平衡靠的是什么?
靠的是:变色,左旋,右旋
变色:节点的颜色由黑变红或者由红变黑
左旋:以某个节点作为支节点旋转节点,其右子节点变成旋转节点的父节点,右子节点的左子节点变成旋转节点的右子节点,左子节点保持不变。
右旋:以某个节点作为支节点旋转节点,其左子节点变成旋转节点的父节点,左子节点的右子节点变成旋转节点的左子节点,右子节点保持不变。
右旋:
void RHR(Node* parent)
{
Node* subL = parent->left;
Node* subLR = subL->right;
parent->left = subLR;
//但是subLR有可能是空的,那么下面这个代码就会崩
if (subLR)
subLR->parent = parent;
Node* parentParent = parent->parent;
subL->right = parent;
parent->parent = subL;
//此时还需要最后一步,把根结点换掉
if (parent == root)
{
root = subL;
root->parent = nullptr;
}
else
{
//此时你需要把30这个结点连接住,但是应该连接在哪一边还需要进行判断
if (parentParent->left == parent)
{
parentParent->left = subL;
}
else
{
parentParent->right = subL;
}
subL->parent = parentParent;
}
}
左旋
void RHL(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 (parent == root)
{
root = subR;
subR->parent = nullptr;
}
else
{
//作为子树的一部分
if (parentParent->left == parent)
{
parentParent->left = subR;
}
else
{
parentParent->right = subR;
}
subR->parent = parentParent;
}
}
红黑树的插入
如果在这颗树插入红节点就不会破坏这棵树的结构,而在12为黑节点就会破坏树的结构,插入红色节点不一定会破坏结构,但是插入黑色节点就一定会破坏树的结构
红黑树的插入包括两个操作:
1,找到要插入的位置
2,插入后的自平衡
注意:插入节点,必须是红节点,理由很简单:红节点在父节点为黑节点时,红黑树的结构没有被破坏,不需要自平衡操作,如果插入的位置的为黑节点,子树的黑节点数+1,就需要自平衡。
插入的情况分为以下几种:
-
红黑树为空树
只需要吧插入节点插入到根节点就行,把根节点设为黑色就行了。
-
插入节点key已存在
处理:更新当前节点的值,为插入节点的值
-
插入节点的父节点为黑节点
由于插入的节点为红节点,当插入节点为黑节点时并不会影响红黑树的平衡,无需自平衡。
如果在这颗树插入红节点就不会破坏这棵树的结构,无需自平衡。 -
插入节点的父节点为黑节点
4.1、叔叔结点存在并且为红结点
依据红黑树性质可知,红色节点不能相连==>祖父结点肯定为黑结点;
因为不可以同时存在两个相连的红结点。那么此时该插入子树的红黑层数的情况是:黑红红。显然最简单的处理方式是把其改为:红黑红处理:
插入节点22把他的父节点和叔叔节点设为黑色,他的爷爷节点设为红色,并后续处理。
4.2、叔叔节点不存在或为红色节点,并且插入节点的父节点为爷爷节点的左子节点
注意:单从插入前来看,叔叔节点非红即空,否则就破坏了红黑树的结构,此路径与其他会多一个黑节点
如果cur节点存在,该红黑树的结构就不符合红黑树的性质。
4.2.1、新插入节点为父节点的左节点(LL双红)
处理:
变色:父节点设为黑节点,爷爷节点设为红色
旋转:以父节点为旋转支点,进行右旋
4.2.2、新插入节点为父节点的右节点(LR双红)
处理:
左旋:以父节点为旋转支点,进行左旋
变色:cur节点设为黑节点,root节点设为红色
右旋:以cur为旋转支点,进行右旋
if (grandfather->left == _parent) {
Node* uncle = grandfather->right;
//4.1的情况叔叔存在且为红
if (uncle && uncle->_col==RED) {
uncle->_col = _parent->_col = BLACK;
grandfather->_col = RED;
//继续往上处理
cur = grandfather;
_parent = cur->parent;
//4.2叔叔不存在或为黑
}else{
//4.2.1
if (cur = _parent->left) {
RHL(grandfather);
_parent->_col = BLACK;
grandfather->_col = RED;
}
//4.2.2
else if(cur = _parent->right){
RHL(_parent);
RHR(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
//4.3
4.3、叔叔节点不存在或为红色节点,并且插入节点的父节点为爷爷节点的右子节点
如果cur节点存在,该红黑树的结构就不符合红黑树的性质。
4.3.1、新插入节点为父节点的右节点(LL双红)
插入节点cur
处理:变色,左旋
变色:把root节点设为红色,把parent设为黑色
左旋:以parent为支点进行左旋
4.3.2、新插入节点为父节点的左节点(LL双红)
处理:
右旋:以parent为支点进行右旋
变色:把root设为红色,把cur设为黑色
左旋:以cur为旋转支点进行左旋
else if (grandfather->right == _parent) {
Node* uncle = grandfather->left;
if (uncle && uncle->_col == BLACK) {
uncle->_col = _parent->_col = BLACK;
grandfather->_col = RED;
cur = grandfather;
_parent = cur->parent;
}else{
if (cur = _parent->right) {
RHL(grandfather);
_parent->_col = BLACK;
grandfather->_col = RED;
}
else if (cur = _parent->left) {
RHR(_parent);
RHL(grandfather);
grandfather->_col = RED;
cur->_col = BLACK;
}
break;
}
}
红黑树与AVL树的比较
红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O( log2N),红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。
红黑树代码
#pragma once
#include<iostream>
using namespace std;
enum color {
RED,
BLACK,
};
template<class K,class V>
struct tree {
tree<K, V> *left;
tree<K, V> *right;
tree<K, V> *parent;
pair<K, V> kv;
color _col;
tree(pair<K, V> _kv)
:kv(_kv)
, left(nullptr)
, right(nullptr)
, parent(nullptr)
,_col(RED)
{}
};
template<class K,class V>
class RHterr {
typedef tree<K, V> Node;
public:
pair<Node*, bool> Instor(pair<K,V> kv) {
if (root == nullptr) {
root = new Node(kv);
root->_col = BLACK;
return make_pair(root, true);
}
Node* _parent = nullptr;
Node* cur = root;
while (cur) {
if (cur->kv.first > kv.first) {
_parent = cur;
cur = cur->left;
}
else if (cur->kv.first < kv.first) {
_parent = cur;
cur = cur->right;
}
else {
return make_pair(cur, false);
}
}
cur = new Node(kv);
if (_parent->kv.first > kv.first) {
_parent->left = cur;
cur->parent = _parent;
}
else {
_parent->right = cur;
cur->parent = _parent;
}
Node* newnode = cur;
while (_parent&&_parent->_col==RED) {
Node* grandfather = _parent->parent;
if (grandfather->left == _parent) {
Node* uncle = grandfather->right;
//4.1的情况叔叔存在且为红
if (uncle && uncle->_col==RED) {
uncle->_col = _parent->_col = BLACK;
grandfather->_col = RED;
//继续往上处理
cur = grandfather;
_parent = cur->parent;
//4.2叔叔不存在或为黑
}else{
//4.2.1
if (cur = _parent->left) {
RHL(grandfather);
_parent->_col = BLACK;
grandfather->_col = RED;
}
//4.2.2
else if(cur = _parent->right){
RHL(_parent);
RHR(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
//4.3
}else if (grandfather->right == _parent) {
Node* uncle = grandfather->left;
if (uncle && uncle->_col == BLACK) {
uncle->_col = _parent->_col = BLACK;
grandfather->_col = RED;
cur = grandfather;
_parent = cur->parent;
}else{
if (cur = _parent->right) {
RHL(grandfather);
_parent->_col = BLACK;
grandfather->_col = RED;
}
else if (cur = _parent->left) {
RHR(_parent);
RHL(grandfather);
grandfather->_col = RED;
cur->_col = BLACK;
}
break;
}
}
}
root->_col = BLACK;
return make_pair(newnode, true);
}
void RHR(Node* parent)
{
Node* subL = parent->left;
Node* subLR = subL->right;
parent->left = subLR;
//但是subLR有可能是空的,那么下面这个代码就会崩
if (subLR)
subLR->parent = parent;
Node* parentParent = parent->parent;
subL->right = parent;
parent->parent = subL;
//此时还需要最后一步,把根结点换掉
if (parent == root)
{
root = subL;
root->parent = nullptr;
}
else
{
//此时你需要把30这个结点连接住,但是应该连接在哪一边还需要进行判断
if (parentParent->left == parent)
{
parentParent->left = subL;
}
else
{
parentParent->right = subL;
}
subL->parent = parentParent;
}
}
void RHL(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 (parent == root)
{
root = subR;
subR->parent = nullptr;
}
else
{
//作为子树的一部分
if (parentParent->left == parent)
{
parentParent->left = subR;
}
else
{
parentParent->right = subR;
}
subR->parent = parentParent;
}
}
bool isBalance(){
//根节点为黑
if (root->_col == RED) {
cout << "不符合红黑树结构" << endl;
return false;
}
int num = 0;
int truenum = 0;
Node* cutt = root;
while(cutt) {
if (cutt->_col == BLACK) {
truenum++;
}
cutt =cutt->left;
}
//检测是否满足红黑树的性质,num用来记录路径中黑色节点的个数
return chackRED(root)//检查红节点
&& chackBLACK(root, num,truenum);
//根不可能是红色的
}
bool chackRED(Node* cur) {
if (cur == nullptr) {
return true;
}
if (cur->_col == RED) {
//检查Cur的父亲是否符合条件
Node* parent = cur->parent;
if (parent->_col == RED) {
cout << "违反限制:存在连续的红节点" << endl;
return false;
}
}
return chackRED(cur->left) && chackRED(cur->right);
//如果左子树违反就不用遍历右子树了
}
//q
bool chackBLACK(Node *root,int BlackNum ,int truenum) {
if (root == nullptr) {
//当走到NULL时,truenum这条路径黑色节点的数量
return BlackNum==truenum;
}
// 统计黑色节点的个数
if (root->_col == BLACK) {
BlackNum++;
}
return chackBLACK(root->left, BlackNum,truenum) &&
chackBLACK(root->right, BlackNum,truenum);
}
//遍历
void _Inorder(Node* root)
{
if (root == nullptr)
return;
_Inorder(root->left);
cout << root->kv.first << " " << root->kv.second << " " << root->_col << endl;
_Inorder(root->right);
}
void Inorder()
{
_Inorder(root);
}
private:
Node* root=nullptr;
};
功能演示代码:
#include"rg.h"
int main() {
int a[] = { 4,5,6,2,7,1,8,0,9 };
RHterr<int, int> r;
for (auto s : a) {
if (s == 1) {
int io = 0;
}
r.Instor(make_pair(s, s));
}
r.Inorder();
//r.isBalance();
}