红黑树和二叉搜索树的区别: 红黑树是二叉搜索树的一种,但是红黑树是一种 “平衡树”。平衡树和普通的二叉搜索树有什么区别呢?
举个栗子:假设有n个节点,普通二叉搜索树 O(h)可能存在 h=n的最坏情况(节点的输入按照从小到大/从大到小);对于红黑树来说O(lgn),不受这种h=n的限制 。
红黑树的性质:
我们将根据上面五条性质作为根据去实现红黑树最关键的插入和删除操作
知识要点:
1、算法中将用NILNode(颜色为黑色的叶节点)代替之前所使用的NULL
2、算法的实现需要保证红黑树的5条性质不被破坏or 破坏了之后能还原
3、二叉树的左旋、右旋
4、红黑树需要有一个记录颜色的属性color
5、黑高:从某个节点到另一个节点的任意一条简单路径上的黑色节点个数称为该节点的黑高
6、根节点到任意叶节点的黑高都相同
红黑树长相如下图
如上图所示,左旋(右图到左图,针对x);右旋(左图到右图,针对y)。
**解密时刻:**左右旋不会破坏二叉树的性质,始终保持α<x<β<y<γ (但是会破坏红黑树的性质哦!所以下面需要修复函数 [插入修复、删除修复])
废话不多说,先上代码
#include <stdio.h>
#include <string.h>
#include <iostream>
using namespace std;
enum NodeColor
{
RED = 1,
BLACK = 2
};
template<class T>
class RBTreeNode
{
public:
RBTreeNode *left;//做孩子
RBTreeNode *right;//右孩子
RBTreeNode *p;//父亲
T mValue;//该节点的值
NodeColor color;//颜色
RBTreeNode(){}
RBTreeNode(T value){
mValue = value;
left = NULL;
right = NULL;
p = NULL;
color = NodeColor(RED);
}
RBTreeNode(T value,RBTreeNode * left,RBTreeNode *right,RBTreeNode *parent,NodeColor color){
mValue = value;
left = left;
right = right;
p = parent;
color = color;
}
~RBTreeNode(){
}
};
template<class T>
class RBSearchTree
{
public:
RBSearchTree();//创建空树
RBSearchTree(RBTreeNode<T> a[]);//传入节点组创建树
RBSearchTree(T a[],int length);//传入节点值组,创建树
~RBSearchTree();
MidWalk();//中序遍历
FrontWalk();//前序遍历
BackWalk();//后序遍历
RBTreeNode<T> * Search(T value);//找到值相等的节点
RBTreeNode<T> * Minimum();//树中最小节点
RBTreeNode<T> * Maximum();//树中最大节点
RBTreeNode<T> * TreePredecessor(RBTreeNode<T> *node);//某节点的前任 狗头.jpg 小于该节点的 值最大节点
RBTreeNode<T> * TreeSuccessor(RBTreeNode<T> *node);//某节点的后驱 大于该节点的 值最小节点
string Insert(T value);//插入
string Delete(T value);//删除
private:
RBTreeNode<T> *root;//根节点
RBTreeNode<T> *NILNode;//叶节点(NIL节点)
MidWalkInner(RBTreeNode<T> *node);//中序遍历
RBTreeInsertFix(RBTreeNode<T> *node);//插入时恢复红黑树性质
RBTreeDeleteFix(RBTreeNode<T> *node);//删除时恢复红黑树性质
RBTreeNode<T> * SearchInner(RBTreeNode<T> *node,T value);//搜索
RBTreeNode<T> * MinimumInner(RBTreeNode<T> *node);//树中最小节点
RBTreeNode<T> * MaximumInner(RBTreeNode<T> *node);//树中最大节点
RBTransplant(RBTreeNode<T> *pNode,RBTreeNode<T> *subNode);//单节点继承,subNode继承pNode原有的位置
LeftRotate(RBTreeNode<T> *node);//左旋
RightRotate(RBTreeNode<T> *node);//右旋
string strTrue;//用于返回true的字符串
string strFalse;//用于返回false的字符串
string strNull;//用于返回null的字符串
};
template<class T>
RBSearchTree<T>::RBSearchTree()
{
strTrue = "true";
strFalse = "false";
strNull = "null";
RBTreeNode<T> *NILNode = new RBTreeNode<T>();
NILNode->color = NodeColor(BLACK);
}
template<class T>
RBSearchTree<T>::RBSearchTree(RBTreeNode<T> a[])
{
strTrue = "true";
strFalse = "false";
strNull = "null";
RBTreeNode<T> *NILNode = new RBTreeNode<T>();
NILNode->color = NodeColor(BLACK);
}
template<class T>
RBSearchTree<T>::RBSearchTree(T a[],int length)
{
strTrue = "true";
strFalse = "false";
strNull = "null";
NILNode = new RBTreeNode<T>();
NILNode->color = NodeColor(BLACK);
//这里直接写成
//root = new RBTreeNode<T>(a[0],NILNode,NILNode,NILNode,NodeColor(BLACK));
//他在识别的时候 left!=right!=p 可能跟引用关系有关 改成&试试??
root = new RBTreeNode<T>(a[0]);
root->p = NILNode;
root->left = NILNode;
root->right = NILNode;
root->color = NodeColor(BLACK);
//大体思路,循环遍历
//小于找左孩子
//大于找右孩子
//孩子为空直接放置
//如果i==0 将root指针指向该节点
for (int i=1;i<length;i++)
{
Insert(a[i]);
}
}
template<class T>
RBSearchTree<T>::MidWalk()
{
RBTreeNode<T> *node = root;
MidWalkInner(node);
cout<<endl;
}
template<class T>
RBSearchTree<T>::MidWalkInner(RBTreeNode<T> *node)
{
if (node!=NILNode)
{
MidWalkInner(node->left);
cout<<node->mValue<<" ";//,"<<node->color<<" p is"<<node->p->mValue<<" ";
MidWalkInner(node->right);
}
}
template<class T>
RBTreeNode<T> * RBSearchTree<T>::Search(T value)
{
RBTreeNode<T> *node = root;
RBTreeNode<T> *matchNode = SearchInner(node,value);
return matchNode;
}
template<class T>
RBTreeNode<T> * RBSearchTree<T>::SearchInner(RBTreeNode<T> *node,T value)
{
if (node==NILNode || node->mValue == value)
{
if (node==NILNode) //对于找不到的给一个-1以示空
return new RBTreeNode<T>(-1);
else
return node;
}
if (value <= node->mValue)
SearchInner(node->left,value);
else
SearchInner(node->right,value);
}
template<class T>
RBTreeNode<T> * RBSearchTree<T>::MinimumInner(RBTreeNode<T> *node)
{
while(node->left!=NILNode)
node = node->left;
return node;
}
template<class T>
string RBSearchTree<T>::Insert(T value)
{
RBTreeNode<T> *insertNode = new RBTreeNode<T>(value,NILNode,NILNode,NILNode,NodeColor(RED));
RBTreeNode<T> *insertNodeParent = NILNode;
RBTreeNode<T> *node = root;
while(node != NILNode)
{
insertNodeParent = node;
if (value <= node->mValue)
node = node->left;
else
node = node->right;
}
insertNode->p = insertNodeParent;
if (value <= insertNodeParent->mValue)
insertNodeParent->left = insertNode;
else
insertNodeParent->right = insertNode;
insertNode->left = NILNode;
insertNode->right = NILNode;
insertNode->color = NodeColor(RED);
RBTreeInsertFix(insertNode);
return strTrue;
}
template<class T>
RBSearchTree<T>::RBTreeInsertFix(RBTreeNode<T> *node)
{
while (node->p->color == NodeColor(RED))
{
if (node->p == node->p->p->left)
{
RBTreeNode<T> *y = node->p->p->right; //叔节点
if (y->color == NodeColor(RED))
{
node->p->color = NodeColor(BLACK);
node->p->p->color = NodeColor(RED);
y->color = NodeColor(BLACK);
node = node->p->p;
}
else if (node == node->p->right)
{
node = node->p;
LeftRotate(node);
}
else
{
node->p->color = NodeColor(BLACK);
node->p->p->color = NodeColor(RED);
RightRotate(node->p->p);
}
}
else
{
RBTreeNode<T> *y = node->p->p->left; //叔节点
if (y->color == NodeColor(RED))
{
node->p->color = NodeColor(BLACK);
node->p->p->color = NodeColor(RED);
y->color = NodeColor(BLACK);
node = node->p->p;
}
else if (node == node->p->left)
{
node = node->p;
RightRotate(node);
}
else
{
node->p->color = NodeColor(BLACK);
node->p->p->color = NodeColor(RED);
LeftRotate(node->p->p);
}
}
}
root->color = NodeColor(BLACK);
}
template<class T>
RBTreeNode<T> * RBSearchTree<T>::Maximum()
{
RBTreeNode<T> *node = root;
while(node->right!=NILNode)
node = node->right;
return node;
}
template<class T>
RBTreeNode<T> * RBSearchTree<T>::MaximumInner(RBTreeNode<T> *node)
{
while(node->right!=NILNode)
node = node->right;
return node;
}
template<class T>
RBTreeNode<T> * RBSearchTree<T>::TreePredecessor(RBTreeNode<T> *node)
{
if(node->left !=NILNode)
{
RBTreeNode<T> * temp = MaximumInner(node->left);
return temp;
}
RBTreeNode<T> * temp = node->p;
while( temp!=NILNode && node == temp->left)
{
node = temp;
temp = temp->p;
}
if (temp == NILNode)
temp = new RBTreeNode<T>(-1); //表示没有前驱
return temp;
}
template<class T>
RBTreeNode<T> * RBSearchTree<T>::TreeSuccessor(RBTreeNode<T> *node)
{
if(node->right !=NILNode)
{
RBTreeNode<T> * temp = MinimumInner(node->right);
return temp;
}
RBTreeNode<T> * temp = node->p;
while( temp!=NILNode && node == temp->right)
{
node = temp;
temp = temp->p;
}
if (temp == NILNode)
temp = new RBTreeNode<T>(-1); //表示没有后继
return temp;
}
template<class T>
string RBSearchTree<T>::Delete(T value)
{
RBTreeNode<T> *node = SearchInner(root,value);//找到要删除的节点
RBTreeNode<T> *backupsNode; //备份指针,用于保存节点长度发生变化的节点(发生继承关系的节点)
NodeColor originColor = node->color;//记录原始颜色(如果删除的是黑色是会影响黑高的,红色则不会)
if(node->left==NILNode) //单左节点
{
backupsNode = node->right;
RBTransplant(node,node->right);
}
else if(node->right==NILNode)//单右节点
{
backupsNode = node->left;
RBTransplant(node,node->left);
}
else
{
//找到后继节点
RBTreeNode<T> *temp = MinimumInner(node->right);
originColor = temp->color;
if (temp->p == node)
backupsNode->p = temp;
else //if (temp->p != node)
{
RBTransplant(temp,temp->right);
temp->right = node->right;
temp->right->p = temp;
}
RBTransplant(node,temp);
temp->left = node->left;
temp->left->p = temp;
temp->color = node->color;
}
if (originColor == NodeColor(BLACK))
RBTreeDeleteFix(backupsNode);
return strTrue;
}
template<class T>
RBSearchTree<T>::RBTreeDeleteFix(RBTreeNode<T> *node)
{
while (node != root && node->color == NodeColor(BLACK))
{
if (node == node->p->left)
{
RBTreeNode<T> *broNode = node->p->right;
if (broNode->color == NodeColor(RED))
{
broNode->color = NodeColor(BLACK);
node->p->color = NodeColor(RED);
LeftRotate(node->p);
broNode = node->p->right;
}
if (broNode->left->color == NodeColor(BLACK) &&broNode->right->color == NodeColor(BLACK))
{
node = node->p;
broNode->color = NodeColor(RED);
}
else if (broNode->left->color == NodeColor(RED))
{
broNode->left->color = NodeColor(BLACK);
broNode->color = NodeColor(RED);
RightRotate(broNode);
broNode = node->p->right;
}
else
{
broNode->right->color == NodeColor(BLACK);
broNode->color = broNode->p->color;
broNode->p->color = NodeColor(BLACK);
LeftRotate(node->p);
node = root;//用于终止循环
}
}
else
{
RBTreeNode<T> *broNode = node->p->left;
if (broNode->color == NodeColor(RED))
{
broNode->color = NodeColor(BLACK);
node->p->color = NodeColor(RED);
RightRotate(node->p);
broNode = node->p->left;
}
if (broNode->left->color == NodeColor(BLACK) &&broNode->right->color == NodeColor(BLACK))
{
node = node->p;
broNode->color = NodeColor(RED);
}
else if (broNode->right->color == NodeColor(RED))
{
broNode->right->color = NodeColor(BLACK);
broNode->color = NodeColor(RED);
RightRotate(broNode);
broNode = node->p->left;
}
else
{
broNode->left->color == NodeColor(BLACK);
broNode->color = broNode->p->color;
broNode->p->color = NodeColor(BLACK);
LeftRotate(node->p);
node = root;//用于终止循环
}
}
}
node->color = NodeColor(BLACK);
}
template<class T>
RBSearchTree<T>::RBTransplant(RBTreeNode<T> *pNode,RBTreeNode<T> *subNode)
{
if(pNode->p == NILNode) //pNode是根节点,则subNode继承到root
root = subNode;
else if (pNode == pNode->p->left)//下面两步,根据pNode是左还是右节点,则subNode继承到对应的节点
pNode->p->left = subNode;
else
pNode->p->right = subNode;
subNode->p = pNode->p;//设置subNode的父节点
}
template<class T>
RBSearchTree<T>::LeftRotate(RBTreeNode<T> *node)
{
RBTreeNode<T> *y = node->right;
node->right = y->left;
if (y->left !=NILNode)
y->left->p = node;
y->p = node->p;
if (node->p == NILNode)
root = y;
else if (node->p->left == node)
node->p->left = y;
else
node->p->right = y;
y->left = node;
node->p = y;
}
template<class T>
RBSearchTree<T>::RightRotate(RBTreeNode<T> *node)
{
RBTreeNode<T> *y = node->left;
node->left = y->right;
if (y->right !=NILNode)
y->right->p = node;
y->p = node->p;
if (node->p == NILNode)
root = y;
else if (node->p->left == node)
node->p->left = y;
else
node->p->right = y;
y->right = node;
node->p = y;
}
int main()
{
int a[11] = {6,5,2,5,7,8,1,3,9,4,11};
RBSearchTree<int> *tr = new RBSearchTree<int>(a,11);
tr->MidWalk();
RBTreeNode<int> *node = tr->Search(2);
cout<<"node value is "<<node->mValue<<endl;
node = tr->TreePredecessor(node);
cout<<"predecessor value is "<<node->mValue<<endl;
node = tr->Search(2);
cout<<"2 node value is "<<node->mValue<<endl;
tr->Delete(2);
node = tr->Search(2);
cout<<"delete 2 node value is "<<node->mValue<<endl;
tr->MidWalk();
tr->Insert(2);
node = tr->Search(2);
cout<<"insert 2 node value is "<<node->mValue<<endl;
tr->MidWalk();
}
代码中大部分与上篇二叉搜索树的相同,除了
1、插入和删除两个函数
2、用NILNode代替了NULL
下面用伪代码对代码中的插入和删除做解析
1、插入
插入的方法与上篇思想都是一样的,不过上一篇使用递归的方式,这一篇使用的循环的方式,并且把所有的NULL替换成了NILNode,注意到代码末将插入节点设置成红色,并且增设了一个“插入修复函数”
①将插入的节点设为红色可以保证红黑树的性质1、3、5不被破坏:
性质1,毋庸置疑
性质3,毋庸置疑,too
性质5,插入的是红色节点,对黑高没有贡献(这就是插入节点设为红色的关键)
接下来看插入修补函数(用于恢复插入节点后导致破坏的红黑树性质)
因为在插入的过程中,可能会导致1、4性质破坏,我们在代码最后一行将根节点设置为黑色,这样一来我们只需要修复性质4的同时保证不破坏其他性质,因为我们插入的节点 z 是红色的,节点的子节点是NILNode节点是黑色的,因此如果出现破坏性质的情况一定是z 、z.p都是红色,即红节点不能有红孩子
整个修复代码我们都在做一件事:直接把红色改成黑色是不可能的,那样会破坏将性质5,因此策略是把多余的红色挪到一个不违反性质4的地方
不能把红色移到兄弟节点上,因为是同个父节点,还是红色,因此应该往叔节点(z.p.p的另一个孩子)上去挪, 有以下8种情况(下面只列出4种,另外4种是对称的 父节点是左孩子及父节点是右孩子各4种)
1、叔节点是红色,且 z == z.p.right(z是右孩子)
2、叔节点是红色,且 z.p == z.p.left (z是左孩子)
3、叔节点是黑色,且 z.p == z.p.right(z是右孩子)
4、叔节点是黑色,且 z == z.p.left(z是左孩子)
对于情况1、2,我们不能把红色挪到对应的叔节点,因此只能把红色上挪,此时不需要进行节点的旋转(只改变了节点的颜色,并无改变节点的value值),因此可以合并成一种情况,因此实际上只有6中情况(下面3种及其对称的3种 父节点是左孩子及父节点是右孩子各3种)
1、叔节点是红色
2、叔节点是黑色,且 z.p == z.p.right(z是右孩子)
3、叔节点是黑色,且 z == z.p.left(z是左孩子)
现在针对上面三种情况逐一分析
情况1: 如伪代码中的 5-8行,
改变{父节点,祖父节点,叔节点}为{红,黑,红},并把z指针往上移至祖父节点
即如下图( a ) -> ( b )的过程,此过程可以保证祖父节点及以下子树红黑树的性质正确
情况2: 如伪代码中的10-11行,
把z指针上移至父节点,并进行一个左旋,
即如下图( b ) -> ( c )的过程,此过程是一个为情况3做铺垫的过程,因为不能通过一次左旋或者一次右旋把右孩子移至叔节点所在的树,并且在此过程中仍旧只破坏了性质4
情况3: 如伪代码中的12-14行,
改变{父节点,祖父节点}为{黑,红},并进行一个右旋
即如下图( c ) ->( d )的过程,经过这个过程就可以将 情况2下的红色挪至叔节点所在的树
2、删除
删除的方法与上篇思想是一样的,
**不同点:1、多出了一个x指针,用于记录“转移节点”(使用过RB_TRANSPLANT的节点)的原始信息 **,因为RB_TRANSPLANT该过程会破坏红黑树的性质,因此要记录该节点的原始信息并用修复函数恢复红黑树性质。
进行12、13行是因为如果不提前保存这个指针,在17行中,x.p的值也会被改变,导致找不到“转移节点”原始信息。
2、把所有的NULL替换成了NILNode,注意到代码末对于y-original-color为黑色增设了一个“删除修复函数
①y-original-color如果为红色,在以下情况
1、 4-5行,删去节点为单左红孩子(y-original-color记录的是删除节点的颜色)
2、 7-8行,删去节点为单右红孩子(y-original-color记录的是删除节点的颜色)
3、 10-20行,此时将后驱节点替换掉删除节点,并且保留了删除节点原有的颜色给后驱节点;后驱节点的右节点嫁接到后驱节点(y-original-color记录的是后驱节点的颜色)
知识回顾,后驱节点的左孩子为空(所以后驱节点也是单孩子喔)
上面三种情况下都是y-original-color记录的节点都是单孩子,并且y-original-color记录的节点都是红色,因此在删除y-original-color节点并且让单孩子向上继承的时候并不会破坏红黑树的性质
但是当y-original-color为黑色的时候,此时红黑树的性质就会被破坏了,至少y节点所在树的性质5被破坏了(影响了y节点所在树的黑高),接下来我们看看修补函数(用于恢复删除节点后导致破坏的红黑树性质)
以下的节点的兄弟,父节点都是相对x节点来说的
在删除的过程中 ,当 y-original-color 为黑色的时候,由于黑高少了一点,我们可以先把这个黑高暂时放在 “转移节点” 上,此时的 “转移节点” 的颜色就是 双重黑 或者 红黑 ,这是不符合性质1的,因此贯穿整个修复算法的思想就是:
思想1、 找到一个 黑色兄弟节点,转移节点 和兄弟同时去掉一个黑色,转移节点 去除多余的黑色变成黑色,兄弟变成红色(黑色去掉一个黑色就是无黑高即红色)
思想2、 让 转移节点 多一个黑节点,而兄弟树上的黑节点不变,这样也能让 转移节点 所在树恢复黑高并且去除 转移节点 多余的黑色
有以下8种情况(下面只列出4种,另外4种是对称的,转移节点是左孩子及转移节点是右孩子各4种)
1、兄弟节点是红色的
2、兄弟节点是黑色,且兄弟节点的两个孩子都是黑色
3、兄弟节点是黑色,且兄弟节点仅右节点是黑色
4、兄弟节点是黑色,且兄弟节点右节点是红色
现在对上述四种情况逐一分析(下图的c、c’表示颜色任意)
情况1: 如伪代码中的5-8行,
改变{父节点,兄弟节点}为{红,黑},并将父节点做一次左旋,
即如下图的( a ), 可以将情况1转化为情况2、3、4 ,此时兄弟树的黑高并无变化并且兄弟树满足红黑树性质
情况2: 如伪代码中的10-11行,
改变{兄弟节点}为{红},同时去掉 “转移节点” 的一个黑高,也就是 思想1 ,为了保证整棵树的黑高,要把去掉的黑高上传到父节点
即如下图的( b ), 此时整棵树满足红黑树性质 ,①当 “情况2” 是由 “情况1” 进入的,新的x节点是红色的,退出循环,并且把去掉情况1去掉的黑高补还给x ②其他情况继续进行循环while
情况3: 如伪代码中的13-16行,
改变{兄弟节点,兄弟左孩子}为{黑,红},并将兄弟节点做一次右旋,
即如下图的( c ), 可以将情况3转化为情况4 ,此时兄弟树的黑高并无变化并且兄弟树满足红黑树性质
情况4: 如伪代码中的17-21行,
改变{父节点,兄弟节点,兄弟右节点}为{黑,原父节点颜色,黑},并将父节点做一次左旋,也就是在 “转移节点” 的树上加多了一个黑节点,也就是 思想2,
即如下图的( d ),此时整棵树满足红黑树性质,21行将x指向根节点,退出循环。
代码运行结果
代码中初始化后的红黑树如下图
删去 2节点后的红黑树如下图
PS.好难哦……哈哈哈哈哈,加油!!!!同理,如果有误的或者写的不好的地方欢迎私信!!一起进步一起努力!