高效实现红黑树的插入
引言:
红黑树是一种特殊的二叉排序树,像java,c++的关联数组,如map都是用红黑树实现。
为什么红黑树应用比原生的二叉排序树(BST)和高度平衡二叉树(AVL)要更为广泛?
答:BST的深度不可控性,比如数据有序插入。不同形态的二叉排序树有不同的性能。
图中a蜕化为线性链表,查找复杂度O(n),b图查找复杂度lg(n)。
而AVL插入、查找的时间复杂度都是lg(n),和红黑树相同。但是AVL是高度平衡的,插入删除更加容易引起不平衡,维护开销大。
所以红黑树是BST和AVL的这种选择,拥有插入,删除,查找的lg(n)时间复杂度。我个人理解,红黑树的查找性能肯定略逊于AVL,但这会在插入和删除开销上得到弥补,所有红黑树的统计性能最高。
红黑树的五个性质
- 每个结点要么是红的,要么是黑的。
- 根结点是黑的。
- 每个叶结点,即空结点(NIL)是黑的。
- 如果一个结点是红的,那么它的俩个儿子都是黑的。
- 对每个结点,从该结点到其子孙结点的所有路径上包含相同数目的黑结点。
红黑树是主要通过性质五来约束树的高度,提高树的平衡性。因为从该结点到其子孙结点的所有路径上包含相同数目的黑结点,所以新插入的结点需要是红结点来保证黑节点数目相等。又通过性质四来维护树的平衡性。
红黑树的插入
红黑树示例
插入动画演示
插入情况及解决方案:
空树
方案:检测是否是根节点,如果是,将颜色改为黑父亲节点p是黑色
方案:不会破坏红黑树的性质,不需要改变父节点P红色,祖父节点G黑色,叔节点U红色
方案:祖父结点必然是黑色,不是已经不满足红黑树的性质了。
将当前节点的父节点和叔叔节点涂黑,祖父结点涂红,把当前结点指向祖父节点(递归平衡,进入新的情况)父节点P红色,祖父节点G黑色,叔节点U黑色,N左叶子
方案:父节点变为黑色,祖父节点变为红色,在祖父节点为支点右旋
(递归平衡,进入新的情况)父节点P红色,祖父节点G黑色,叔节点U黑色,N右叶子
方案:以新当前节点的父节点左旋,当前节点的父节点做为新的当前节点(递归平衡,进入新的情况)
建议参考优秀博客,对入插入情况非常详细:点这里查看
配合上面的红黑树的插入视频,把几种情况弄懂非常容易。
附上c的源码,希望对您有些帮助,核心代码不过20行。
/*2016-8-30 hiluo-scu*/
#include <stdio.h>
#include <stdlib.h>
typedef struct node* link;
link null=NULL;
struct node {
int item;
link l;
link r;
int red;
};
link NODE(int item,link l,link r,int red)
{
link t = (link)malloc(sizeof(struct node));
t->item = item;
t->l = l;
t->r = r;
t->red = red;
return t;
}
//初始化简历哨兵结点(叶节点或NULL结点)
link RB_init()
{
null = NODE(0,NULL,NULL,0);
null->l = null;null->r=null;
return null;
}
//线序遍历
void pprintf(link t)
{
if(t!=null)
{
printf("%d%c \n",t->item,t->red?'+':' ');
pprintf(t->l);
pprintf(t->r);
}
}
//中序遍历
void IN_pprintf(link t)
{
if(t!=null)
{
IN_pprintf(t->l);
printf("%d%c \n",t->item,t->red?'+':' ');
IN_pprintf(t->r);
}
}
link rotR(link t)
{
link x = t->l;t->l = x->r; x->r = t;
return x;
}
link rotL(link t)
{
link x = t->r;t->r = x->l; x->l = t;
return x;
}
//核心代码 高效地实现红黑树插入
link insert_node(link t,int item,int sw)
{
if(t==null) return NODE(item,null,null,1);
if( (t->l->red==1) && (t->r->red==1) )
{
t->red = 1;
t->l->red = 0;
t->r->red = 0;
}
if(item < t->item)
{
t->l = insert_node(t->l,item,0);
if(t->red && t->l->red &&sw) t=rotR(t);
if(t->l->red && t->l->l->red)
{
t=rotR(t);t->red = 0;t->r->red = 1;
}
}else{
t->r = insert_node(t->r,item,1);
if(t->red && t->r->red && !sw) t=rotL(t);
if(t->r->red && t->r->r->red)
{
t=rotL(t);
t->red = 0;
t->l->red = 1;
}
}
return t;
}
link RB_insert(link root,int item)
{
root = insert_node(root,item,0);
root->red = 0;
return root;
}
/*2016-8-30 hiluo-scu*/
int main()
{
int N = 14;
int a[]={10,85,15,70,20,60,30,50,65,80,90,40,5,55};
//srand(time(NULL));
link root = RB_init();
for(int i=0;i<N;i++)
{
root = RB_insert(root,a[i]);
}
printf("\t---线序遍历tree------(+表示红色结点)\n");
pprintf(root);
printf("\t---中序遍历tree------(+表示红色结点)\n");
IN_pprintf(root);
return 0;
}
参考博客:
http://blog.csdn.net/v_JULY_v/article/details/6105630
http://blog.csdn.net/eric491179912/article/details/6179908
感谢!