zoukankan      html  css  js  c++  java
  • 红黑树的插入(算法导论)

      红黑树是一棵二叉搜索树,它在每个节点增加了一个存储位来表示节点的颜色。一颗红黑树是满足下面红黑性质的二查搜索树:

    1)每个节点或是红色的,或是黑色的

    2)根节点是黑色的

    3)每个叶节点(NIL)是黑色的

    4)如果一个节点是红色的,则它的两个子节点都是黑色的

    5)对每个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点。

             对一颗有n个内部节点的红黑树,其高度至多为2lg(n+1)。这样可以保证红黑树的查找的时间复杂度控制在O(lgn)。

             对每一颗红黑树,我们假如所有的叶子节点都为空,并且都是黑色的。而任何非空节点都是内部节点,并且只能是红色或黑色。

     1、红黑树的插入

             在红黑树的插入中,默认插入的节点是红色的,因为假如插入的节点为黑色的,则会导致红黑树的黑高不等,若是红色的则不会出现此问题,但是这时有可能出现插入节点的父节点也是红色的,这样就破坏了红黑树的第四条性质。这时就需要对红黑树进行调整。

     

     红黑树的插入总共六种情况,其中三种与另外三种对称。假如要插入的节点为z,则

    在此我们主要介绍z的父节点是其祖父节点的左孩子的情况。这种情况下又有三种情况,分别为:

             1)z的叔叔节点y是红色的。这时将z的父节点和z的叔叔节点y都修改成黑色的,z的祖父节点A原来一定是黑色的,这时将它修改为红色的,并将新的z节点指向z的祖父节点,然后继续向上进行调整。


    图1.1 z的叔叔节点为红色的情况

     

             2)z的叔叔节点是黑色的,并且z是一个右孩子节点。此时,首先将z指针指向其父节点,然后以此时z指向的节点为中心进行左旋,于是将此种情况转化成了Case 3的情形。即z节点是左孩子节点。

    图1.2 z的叔叔节点为黑色并且z为右孩子


             3)z的叔叔节点是黑色的,并且z是一个左孩子节点。此时首先将z的父节点修改为黑色,然后将其祖父节点修改为红色,然后以z的祖父节点为中心进行右旋。

     

    图1.3 z的叔叔节点为黑色并且z为左孩子


    在调整结束后,需要将根节点的颜色重新更改为黑色

    关于红黑树的创建,插入以及调整的C代码如下:

    #include <stdio.h>
    #include <stdlib.h>
    typedef struct Node
    {
    	int key, color;					//color = 0:red, 1:black
    	struct Node *left, *right, *p;
    }NODE, *TREE;
    
    TREE rb_insert_fixup(TREE r, NODE *z)
    {
    	TREE y;
    	y = NULL;
    	while(z->p!=NULL && z->p->color == 0)
    	{
    		if(z->p == z->p->p->left)	//z的父节点是其祖父节点的左孩子, 此种情形有三种情况 
    		{
    			y = z->p->p->right;
    			if(y!=NULL && y->color == 0)	//case1: z的叔叔节点为红色 
    			{
    				z->p->color = 1;
    				y->color = 1;
    				z->p->p->color = 0;
    				z = z->p->p;
    			}
    			else
    			{
    				if(z == z->p->right)	//case2: z的叔叔节点为黑色,并且z是右孩子 ,此时左旋后变成case3的情形 
    				{
    					z = z->p;
    					r = left_rotate(r, z);
    				}
    									//case3: z的叔叔节点为黑色,并且z是左孩子 
    				z->p->color = 1;
    				z->p->p->color = 0;
    				r = right_rotate(r, z->p->p);
    			}
    		}
    		else					//z的父节点是其祖父节点的右孩子,此种情形也有三种情况,并且与上面三种情况对称 
    		{
    			y = z->p->p->left;
    			if(y->color == 0)
    			{
    				z->p->color = 1;
    				y->color = 1;
    				z->p->p->color = 0;
    				z = z->p->p;
    			}
    			else
    			{
    				if(z == z->p->left)
    				{
    					z = z->p;
    					r = right_rotate(r, z);
    				}
    				z->p->color = 1;
    				z->p->p->color = 0;
    				r = left_rotate(r, z->p->p);
    			}
    		}
    	}
    	r->color = 1;
    	return r;
    }
    
    TREE rb_insert(TREE r, NODE *z)
    {
    	TREE x, y;
    	x = r;
    	y = NULL;
    	while(x != NULL)
    	{
    		y = x;
    		if(z->key < x->key)
    			x = x->left;
    		else 
    			x = x->right;
    	}
    	
    	z->p = y;
    	if(y == NULL)
    	{
    		r = z;
    	}
    	else if(z->key < y->key)
    	{
    		y->left = z;
    	}
    	else 
    	{
    		y->right = z;
    	}
    	z->left = NULL;
    	z->right = NULL;
    	z->color = 0;
    	
    	r = rb_insert_fixup(r, z);
    	return r;
    }
    
    TREE rb_create(int arr[], int n)
    {
    	TREE r, tmp;
    	int i;
    	r = NULL;
    	for(i=0; i<n; i++)
    	{
    		tmp = (TREE)malloc(sizeof(NODE));
    		tmp->key = arr[i];
    		r = rb_insert(r, tmp);
    	}
    	return r;
    }
    void print_pre(TREE r)
    {
    	if(r == NULL)
    		return ;
    	if(r->color == 0)
    		printf("%d:red  ", r->key);
    	else
    		printf("%d:black  ", r->key);
    	print_pre(r->left);
    	print_pre(r->right);
    }
    void print_mid(TREE r)
    {
    	if(r == NULL)
    		return ;
    	print_mid(r->left);
    	if(r->color == 0)
    		printf("%d:red  ", r->key);
    	else
    		printf("%d:black  ", r->key);
    	print_mid(r->right);
    }
    int main()
    {
    	TREE r;
    	int n = 6, arr[6]={41, 38, 31, 12, 19, 8};
    	//create a rb tree
    	r = rb_create(arr, n);
    	
    	printf("----------先序遍历----------:
    ");
    	print_pre(r);
    	
    	printf("
    
    ----------中序遍历----------:
    ");
    	print_mid(r);
    	printf("
    
    ----------End line----------
    ");
    	
    	return 0;
    }
    

     2、旋转

    现在我们介绍一下刚才用到的旋转,旋转有两种:左旋和右旋。其左旋和右旋过程分别图下图所示。

    图2.1 右旋

    右旋的C代码如下:

    TREE right_rotate(TREE r, NODE *x)
    {
    	TREE y = x->left;
    	x->left = y->right;
    	if(y->right != NULL)
    		y->right->p = x;
    	y->p = x->p;
    	if(x->p == NULL)
    		r = y;
    	else if(x->p->left = x)
    		x->p->left = y;
    	else
    		x->p->right = y;
    	y->right = x;
    	x->p = y;
    	
    	return r;
    }



    图2.2 左旋

    左旋的C代码如下:

    TREE left_rotate(TREE r, NODE *x)
    {
    	TREE y = x->right;
    	x->right = y->left;
    	if(y->left != NULL)
    		y->left->p = x;
    	y->p = x->p;
    	if(x->p == NULL)
    		r = y;
    	else if(x == x->p->left)
    		x->p->left = y;
    	else 
    		x->p->right = y;
    	y->left = x;
    	x->p = y;
    	
    	return r;
    }


  • 相关阅读:
    oracle删除用户及其名下对象
    CENTOS7设置显示中文
    hadoop安装
    linux使用flock文件锁解决crontab冲突问题
    Hive On Spark和SparkSQL
    MapReduce和Tez对比
    安装python的redis模块
    拷贝一个用户下的所有表和数据到另外一个库
    java学习笔记10--泛型总结
    java学习笔记9--内部类总结
  • 原文地址:https://www.cnblogs.com/liuwu265/p/4100097.html
Copyright © 2011-2022 走看看