赞
踩
红黑树是一棵二叉搜索树,它在每个节点上增加了一个存储位来表示节点的颜色,可以是Red或Black。通过对任何一条从根到叶子简单路径上的颜色来约束,红黑树保证最长路径不超过最短路径的两倍,因而近似于平衡。
红黑树是满足下面红黑性质的二叉搜索树
1. 每个节点,不是红色就是黑色的
2. 根节点是黑色的
3. 如果一个节点是红色的,则它的两个子节点是黑色的
4. 对每个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点。
为什么满足上面的颜色约束性质,红黑树能保证最长路径不超过最短路径的两倍?
首先每条路径上的黑色结点向相同,假入一颗树有n个黑色结点,那么这个红黑树的最短路径就是n,最长路径就是2n(由于红色结点不能连续,所以最长路径中,红色结点都是插在两个黑色结点中间,红色结点最多也是有n个,所以最长路径就是最短路径的两倍),所以可以保证最长路径不超过最短路径的两倍;
如上图所示情况,cur为红且存在,p为红且存在,g为黑且存在,u存在且为黑;
首先根据规则,不能出现连续的红色结点,所以讲P变为黑色;然后g结点的右子树多出一个黑色结点,会破坏每条路径黑色结点相同的规则,此时的u为黑色,所以解决办法就是针对g右旋之后,将p调整为根结点,此时以P为根结点的左子树黑色结点没有增加,和全局的每条路径的黑色结点相同,但是通过旋转,g变为了以P为根结点的右子树上的结点,这时,P的右子树的黑色结点增加1,不符合规则,所以将g变为红色;
同理左旋也一样;
判断一颗树是否为红黑树,判断思路:
1.根结点必须为黑色结点—直接检查
2.不能有连续的红结点 —每个结点和其father不同时为红色
3.每条路径黑色结点数目相同—先求出一个路径的黑色结点作为基准,然后判断其他路径的黑结点数目是否和其相同;采用传值的方式递归判断,每一层不影响上一层的黑色结点统计的数目;
:
#pragma once
#include<iostream>
using namespace std;
enum Colour
{
RED,//红色
BLACK,//黑色
};
template<typename K,typename V>
struct RBTreeNode
{
K _key;//关键字
V _value;//关键字所指的值
RBTreeNode<K,V>* _left;//左孩子
RBTreeNode<K,V>* _right;//右孩子
RBTreeNode<K,V>* _father;//父亲
Colour _col;//颜色
RBTreeNode(const K& key,const V& value)//构造函数
:_key(key)
,_value(value)
,_left(NULL)
,_right(NULL)
,_father(NULL)
,_col(RED)//默认添加的元素为红色,不影响该路径的黑色结点的数量
{}
};
template<typename K,typename V>
class RBTree
{
typedef RBTreeNode<K,V> Node;
public:
RBTree()//构造函数
:_root(NULL)
{}
bool Insert(const K& key,const V& value)//插入
{
//1.空树插入节点
if (_root==NULL)
{
_root=new Node(key,value);
//1.1改变根结点的颜色为黑色
_root->_col=BLACK;
//1.2插入完成,直接返回
return true;
}
//2.插入的不是空树
//2.1寻找插入的位置
Node* cur=_root;//遍历红黑树,寻找并记录插入的位置
Node* father;//记录插入位置的父节点
while (cur)
{
if (cur->_key>key)
{
father=cur;
cur=cur->_left;
}
else if (cur->_key < key)
{
father=cur;
cur=cur->_right;
}
else
{
return false;
}
}
Node* node=cur;//记录要插入的位置,方便返回,针对返回pair结构
//2.2寻找到插入的位置,进行插入
cur=new Node(key,value);
if (father->_key>key)
{
father->_left=cur;
}
else
{
father->_right=cur;
}
cur->_father=father;
//2.3插入完成之后,开始判断是否需要调整颜色以及进行旋转
//当father为空,说明更新调整到了根结点,停止;当father的颜色为黑色;插入或者更新不影响上层,直接结束
//进行调整的情况全建立在father存在且为红的情况
while (father&&father->_col==RED)
{
//进入循环的场景是cur为红,父节点为红,祖父节点为黑,所以只需要根结点叔叔节点的是否存在或者颜色
//以及cur和father是各自父节点的左右孩子进行相应的调整
//2.3.1先定义祖父和叔叔节点
//先定义祖父节点
Node* grandfather=father->_father;
//在定义叔叔节点
Node* uncle=NULL;
//因为不知道叔叔节点是祖父的左孩子还是右孩子,所以需要判断
//叔叔节点为祖父的右孩子
if (father==grandfather->_left)
{
uncle=grandfather->_right;
// 1.叔叔存在,且为红,进行变色处理
if (uncle&&uncle->_col==RED)
{
//改变颜色
father->_col=uncle->_col=BLACK;
grandfather->_col=RED;
//向上检查调整
cur=grandfather;
father=cur->_father;
}
// 2/3.叔叔存在且为黑,或者不存在
else// uncle->_col==BLACK||uncle==NULL
{
if (father->_right=cur)//双旋
{
RotateL(father);
swap(father,cur);//更正父子关系
}
RotateR(grandfather);
father->_col=BLACK;
grandfather->_col=RED;
break;
}
}
else//father==grandfather->_right
{
uncle=grandfather->_left;
// 1.叔叔存在,且为红,进行变色处理
if (uncle&&uncle->_col==RED)
{
//改变颜色
father->_col=uncle->_col=BLACK;
grandfather->_col=RED;
//向上检查
cur=grandfather;
father=cur->_father;
}
// 2/3.叔叔存在且为黑,或者不存在
else //uncle->_col==BLACK||uncle==NULL
{
if (father->_left==cur)// 双旋
{
RotateR(father);
swap(father,cur);//更正父子关系
}
RotateL(grandfather);
father->_col=BLACK;
grandfather->_col=RED;
break;
}
}
}
_root->_col=BLACK;
return true;
}
void Inorder()//中序遍历
{
_Inorder(_root);
}
//判断一颗树是否为红黑树
//判断思路:1.根结点必须为黑色结点---直接检查
//2.不能有连续的红结点 ---每个结点和其father不同时为红色
//3.每条路径黑色结点数目相同---先求出一个路径的黑色结点作为基准,然后判断其他路径的黑结点数目是否和其相同;
bool IsBlance()
{
//1.根结点为红,不是红黑树
if (_root&&_root->_col==RED)
{
return false;
}
//2.求出一个路径的黑色结点作为基准值
int k=0;
int num=0;
Node* cur=_root;
while (cur)
{
if (cur->_col==BLACK)
{
++k;
}
cur=cur->_left;
}
return _CheakColour(_root)&&_CheakNum(_root,k,num);
}
private:
//检查颜色--不能出现两个连续红结点
bool _CheakColour(Node* root)
{
//空树是红黑树
if (root==NULL)
{
return true;
}
//两个连续的红结点,不是红黑树
if (root->_col==RED && root->_father->_col==RED)
{
return false;
}
return _CheakColour(root->_left)&&_CheakColour(root->_right);
}
//判断每个路径的黑色结点是否和基准值相等
bool _CheakNum(Node* root,int k,int num)
{
if (root==NULL)
{
return true;
}
if (root->_col==BLACK)
{
++num;
}
if (root->_left==NULL&&root->_right==NULL&&num!=k)
{
return false;
}
return _CheakNum(root->_left,k,num)&&_CheakNum(root->_right,k,num);
}
//中序递归打印
void _Inorder(Node* root)
{
if (root==NULL)
{
return ;
}
_Inorder(root->_left);
cout<<root->_key<<"-"<<root->_value<<endl;
_Inorder(root->_right);
}
//左旋
void RotateL(Node* node)
{
Node* SubR=node->_right;
Node* SubRL=SubR->_left;
Node* PPNode=node->_father;
//1.连接node和subRL
node->_right=SubRL;
if (SubRL)
{
SubRL->_father=node;
}
//2.连接PPNode和SubR
if (PPNode==NULL)
{
_root=SubR;
SubR->_father=PPNode;
}
else//PPNode!=NULL
{
if (PPNode->_left==node)
{
PPNode->_left=SubR;
}
else
{
PPNode->_right=SubR;
}
SubR->_father=PPNode;
}
//3.连接带有SubRL的node和SubR
node->_father=SubR;
SubR->_left=node;
}
//右旋
void RotateR(Node* node)
{
Node* SubL=node->_left;
Node* SubLR=SubL->_right;
Node* PPNode=node->_father;
//1.连接node和SubLR
node->_left=SubLR;
if (SubLR)
{
SubLR->_father=node;
}
//2.连接PPNode和SubL
if (PPNode==NULL)
{
_root=SubL;
SubL->_father=PPNode;
}
else
{
if (PPNode->_left==node)
{
PPNode->_left=SubL;
}
else
{
PPNode->_right=SubL;
}
SubL->_father=PPNode;
}
//3.连接带有SubLR的node和SubL
node->_father=SubL;
SubL->_right=node;
}
private:
Node* _root;
};
int main()
{
RBTree<int,int> t;
t.Insert(12,12);
cout<<t.IsBlance()<<endl;
t.Insert(6,6);
cout<<t.IsBlance()<<endl;
t.Insert(15,15);
cout<<t.IsBlance()<<endl;
t.Insert(3,3);
cout<<t.IsBlance()<<endl;
t.Insert(13,13);
cout<<t.IsBlance()<<endl;
t.Insert(8,8);
cout<<t.IsBlance()<<endl;
t.Insert(22,22);
cout<<t.IsBlance()<<endl;
t.Insert(5,5);
cout<<t.IsBlance()<<endl;
t.Insert(4,4);
cout<<t.IsBlance()<<endl;
t.Inorder();
return 0;
}

红黑树和AVL树都是高效的平衡二叉树,增删查改的时间复杂度都是O(lg(N))
红黑树不追求完全平衡,只要保证最长路径不超过最短路径的2倍即可,所以相对于AVL树要保持高度差不能超过1的高度平衡而言,红黑树降低了旋转的要求,旋转的次数比AVL少,所以性能会优于AVL树,所以实际运用中红黑树更多。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。