zoukankan      html  css  js  c++  java
  • 红黑树

    下面网址中是红黑树很好的教材,很详细。

    http://blog.csdn.net/eric491179912/article/details/6179908

    另外经典教材就是算法导论中关于红黑树的章节了,以算法导论为主,实在不明白的地方再去网上查找资料。

    红黑树是一种特殊的二叉查找树,大家都知道二叉查找树的复杂度最坏情况为O(h),为了减低最坏情况的复杂度,大神们设计了许多种二叉查找树的改进版本,红黑树便是其中之一,它可以保证在最坏情况下复杂度为O(lgn),也就是说h < lgn。

    红黑树的五个性质

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

    2)根节点是黑色

    3)每个叶子节点是黑色的(此处的叶子节点指的是外部节点)

    4)红色节点的两个孩子都是黑色的

    5)对于每个节点,从该节点到其子孙叶子节点的所有路径上包含相同数目的黑色节点

    红黑树节点的定义

    typedef struct node
    {
    	int color;
    	int key;
    	struct node *p;//指向父节点
    	struct node *left, *right;
    
    }RBtreeNode;
    

    与二叉搜索树相比较,多出了2部分,本节点的颜色属性和指向父节点是指针,之所以需要指向父节点是为了后序操作的便利。

    红黑树的左旋和右旋

    在节点x上左旋,是以x和x的右孩子y之间的轴为支点,旋转结束后x成为y的左孩子;在节点y上右旋,是以y和y的左孩子x之间的轴为支点,旋转结束后y是x的右孩子;如图所示

       ,理论上与二叉搜索树的旋转没有任何区别

    红黑树的插入

    假设新插入节点z,我们将其置为红色,可能破坏性质2)和性质4),违反性质2)是因为新插入的点成为了根节点,违反性质4)是因为z->p也是红色;插入z后,从底向上逐层调整树的结构和颜色,使其仍然满足红黑树的性质。既然从底层开始调整,我们可以只考虑违反性质4),当然在调整过程中绝不允许出现违反其他性质的现象。

    前提z的父亲是红色

    case1--z的叔叔是红色

    z的叔叔uncle_z是红色,那z的爷爷肯定是黑色,可以把z的父亲和叔叔置为黑色,z的爷爷置为红色,z <- z的爷爷,然后继续向上操作。

    case2--uncle_z是黑色,且z和uncle_z不是同一方向的孩子,即z是左孩子,uncle_z是右孩子;或者z是右孩子,uncle_z是左孩子

      case2.1--z是左孩子,uncle_z是右孩子,如下面左图所示。我们以z的爷爷为输入,做一次右旋操作,同时把parent_z置为黑色,gparent_z置为红色,整棵树调整结束。

         左右分割   

      case2.2--z是右孩子,uncle_z是左孩子,如上面右图所示。我们,以z的爷爷为输入,做一次左旋操作,同时把parent_z置为黑色,gparent_z置为红色,整棵树调整结束。

     可以看到,case2.1和case2.2是对称的两种情况。

    case3--uncle_z是黑色,且z和uncle_z是同一方向的孩子,即z是左孩子,uncle_z也是左孩子;或者z是右孩子,uncle_z也是右孩子

       case3.1--z和uncle_z同时是左孩子,如下面左图所示。我们以z的父亲为输入,做一次右旋操作,同时把z置为旋转前z的父亲,变成了case2.2,有木有!!

      左右分割  

      case3.2--z和uncle_z同时是右孩子,如上面右图所示。我们以z的父亲为输入,做一次左旋操作,同时把z置为旋转前z的父亲,变成了case2.1。

    插入的最后,在把root的颜色置为黑色,完事。

     红黑树的删除

    假设被删除节点是z,可以找到真正要被删除的节点y,即如果z至多有一个孩子,y等于z;否则y是z的中序后继,此时y至多有一个右孩子;总的说来y至多只有一个右孩子x。如果y是红色,删除y后仍然能够保证红黑树的5个性质;如果y是黑色,可能违反性质2),4),5)。删除y时就是把x代替了y的位置,我们可以给x额外增加一个黑色,使其成为了双黑色和红黑色,这样满足了性质5),但违反性质1)。

    如果x是红黑色,我们将其置为黑色,调整结束;

    如果x是根节点,我们将其置为黑色,结束;

    如果x是双黑色,做必要的旋转和涂色。

    假设parent_x为删除节点y后x的父节点,实际上就是y的父节点,那么parent_x一定是有两个孩子的,因为此时y非空,且y是黑色,在不违反性质5)的前提下,y一定有兄弟。令parent_x的另一个孩子为w,即x的兄弟为w

    case1--w为黑色,且w的两个孩子都是黑色

    因为w是黑色,x是双重黑色,可以同时去掉x和w的黑色,x的父节点增加一重黑色,不会违反性质5),然后把x赋值给x的父节点,继续循环。如下面两幅图所示

        左右分割  

     case2--w是黑色,且w同一方向的孩子是红色,即若w是左孩子,w的左孩子是红色;若w是右孩子,w的右孩子是红色。我们可以通过旋转使x的高度减一,并且在x和x的父亲之间增加一个黑色节点,最后把x置为根节点,循环结束

      case2.1--w是黑色,w是左孩子,w的左孩子是红色,以w的父节点为输入,做一次左旋,并且做一些颜色涂改

       左右分割   

       case2.2--w是黑色,w是右孩子,w的右孩子是红色,以w的父节点为输入,做一次右旋,并且做一些颜色涂改

     case3--w是黑色,w同一方向的孩子是黑色,我们可以通过旋转使其转化为case2

      case3.1--w是黑色,w是左孩子,w的左孩子是黑色,那么右孩子一定是红色,否则就成了case1;以w为输入,做一次右旋,变成了case2.1

        左右分割  

      case3.2--w是黑色,w是右孩子,w的右孩子是黑色,那么左孩子一定是红色,否则就成了case1;以w为输入,做一次左旋,变成了case2.2

      case4--w是红色的,则w肯定有黑孩子,parent_x是黑色的,以parent_x为输入,做一次左旋,可转变成case1,case2,或者case3

     

     附源代码,另附测试数据的大神博客网址:http://blog.csdn.net/v_JULY_v/article/details/6284050

    //红黑树基本操作
    #include <stdio.h>
    #include <list>
    
    #define BLACK 0
    #define RED 1
    typedef struct node
    {
    	int color;
    	int key;
    	struct node *p;
    	struct node *left, *right;
    
    }treeNode;
    
    //在节点x上左旋操作,以x和x右孩子之间的轴为支点,旋转结束后x成为y的左孩子
    //      x                  y
    //     /    left_rotate  /  
    //    lx  y     --->     x  ry
    //       /             / 
    //      ly ry          lx ly
    treeNode* left_rotate(treeNode* root, treeNode* x)
    {
    	treeNode *y = x->right;
    	x->right = y->left;
    	if(y->left != NULL)
    		(y->left)->p = x;
    	y->p = x->p;
    	if(x->p == NULL)//x是根节点
    		root = y;
    	else if(x == (x->p)->left)
    		(x->p)->left = y;
    	else (x->p)->right = y;
    	y->left = x;
    	x->p = y;
    
    	return root;
    }
    
    //在节点y上的右旋操作,以y和y的左孩子x之间的轴为支点,旋转结束后y是x的右孩子
    //      y                  x
    //     /   right_rotate  /  
    //    x  ry     --->     lx  y
    //   /                     /           
    //  lx rx                  rx ry
    treeNode* right_rotate(treeNode* root, treeNode* y)
    {
    	treeNode *x = y->left;
    	y->left = x->right;
    	if(x->right != NULL)
    		(x->right)->p = y;
    	x->p = y->p;
    	if(y->p == NULL)//y是根节点
    		root = x;
    	else if(y == (y->p)->left)
    		(y->p)->left = x;
    	else (y->p)->right = x;
    	x->right = y;
    	y->p = x;
    	return root;
    }
    
    //插入新的节点后必须得调整
    treeNode* rb_insert_fixup(treeNode *root, treeNode *z)
    {
    	treeNode *uncle_z;
    	while(z->p && z->p->color == RED && z->p->p)
    	{
    		if (z->p->p->left == z->p)//z的父亲是左孩子
    		{
    			uncle_z = z->p->p->right;//z的叔父是右孩子
    			//case1--叔父是右孩子,红色
    			if(uncle_z && uncle_z->color == RED)
    			{
    				uncle_z->color = BLACK;
    				z->p->color = BLACK;
    				z->p->p->color = RED;
    				z = z->p->p;
    			}
    			//case1 end
    			else
    			{	
    				//case2--叔父是右孩子,黑色 z是右孩子
    				if (z == z->p->right)//如果z是右孩子
    				{
    					z = z->p;
    					root = left_rotate(root, z);
    				}
    				//case2 end 
    				//case3--叔父是右孩子,黑色 z是左孩子
    				z->p->color = BLACK;
    				z->p->p->color = RED;
    				root = right_rotate(root, z->p->p);
    				//case3 end
    			}
    		}
    		else//z的父亲是右孩子
    		{
    			uncle_z = z->p->p->left;//z的叔父是左孩子
    			//case4--叔父是左孩子,红色
    			if (uncle_z && uncle_z->color == RED)
    			{
    				uncle_z->color = BLACK;
    				z->p->color = BLACK;
    				z->p->p->color = RED;
    				z = z->p->p;
    			}
    			//case4 end
    			else 
    			{	
    				//case5--叔父是左孩子,黑色,z是左孩子
    				if (z == z->p->left)
    				{
    					z = z->p;
    					root = right_rotate(root, z);
    				}
    				//case5 end
    				//case6--叔父是左孩子,黑色 z是右孩子
    				z->p->color = BLACK;
    				z->p->p->color = RED;
    				root = left_rotate(root, z->p->p);
    				//case6 end
    			}
    		}
    	}
    	root->color = BLACK;
    	return root;
    }
    
    //插入新的节点
    treeNode* rb_insert(treeNode *root, int value)
    {
    	treeNode *z = new treeNode;
    	z->key = value;
    	treeNode *x = root;
    	treeNode *y = NULL;
    
    	while(x != NULL)
    	{
    		y = x;
    		if(value == x->key)
    			return root;
    		if(value < x->key)
    			x = x->left;
    		else x = x->right;
    	}
    	z->p = y;
    	if (y == NULL)
    	{
    		root = z;
    	}
    	else
    	{
    		if(value < y->key)
    			y->left = z;
    		else
    			y->right = z;
    	}
    	z->left = NULL;
    	z->right = NULL;
    	z->color = RED;
    	root = rb_insert_fixup(root, z);
    	return root;
    }
    //删除节点x的父节点后调整, x相当于多重黑色或者红黑色
    treeNode* rb_delete_fixup(treeNode *root, treeNode *x, treeNode *parent_x)
    {
    	treeNode *w;//x_brother
    	while(x!=root && (x == NULL ||x->color == BLACK))
    	{
    		//x的父亲parent_x一定有两个孩子
    		if(x == parent_x->left)
    		{
    			w = parent_x->right;//x的右兄弟w
    			//case1--x的兄弟w为红色,则w肯定有黑孩子,左旋,转换成case2,case3....
    			if(w->color == RED)
    			{
    				w->color = BLACK;
    				parent_x->color = RED;
    				root = left_rotate(root, parent_x);
    				w = parent_x->right; //w重新置为parent_x的右孩子
    			}
    			//case1 end
    			//case2--w为黑色,w的两个孩子(如果有)也是黑色
    			if((w->left == NULL || w->left->color == BLACK) && (w->right == NULL || w->right->color == BLACK))
    			{
    				w->color = RED;
    				x = parent_x;
    				parent_x = parent_x->p;
    			}
    			//case2 end	
    			else
    			{	//case3--w的右孩子是黑色,左孩子是红色
    				if(w->right == NULL || w->right->color == BLACK)
    				{
    					if(w->left != NULL)//如果w有左孩子,一定是红色
    						w->left->color = BLACK;
    					w->color = RED;
    					root = right_rotate(root, w);
    					w = parent_x->right;
    				}
    				//case3 end
    				//case4 w的右孩子是红色
    				if (w->right != NULL)//如果w有右孩子,一定是红色
    					w->right->color = BLACK;
    				w->color = parent_x->color;
    				parent_x->color = BLACK;	
    				root = left_rotate(root, parent_x);
    				x = root;
    			}
    		}
    		else//与上面对称
    		{
    			w = parent_x->left;//x的左兄弟w
    			//case5--x的兄弟w为红色,则w肯定有黑孩子,右旋,转换成case2,case3....
    			if(w->color == RED)
    			{
    				w->color = BLACK;
    				parent_x->color = RED;
    				root = right_rotate(root, parent_x);
    				w = parent_x->left;
    			}
    			//case5 end
    			//case6--w为黑色,w的两个孩子(如果有)也是黑色
    			if((w->left == NULL || w->left->color == BLACK) && (w->right == NULL || w->right->color == BLACK))
    			{
    				w->color = RED;
    				x = parent_x;
    				parent_x = parent_x->p;
    			}
    			//case6 end
    			else
    			{
    			//case7--w的左孩子是黑色,右孩子是红色
    				if(w->left == NULL || w->left->color == BLACK)
    				{
    					if(w->right != NULL)
    						w->right->color = BLACK;
    					w->color = RED;
    					root = left_rotate(root, w);
    					w = parent_x->left;
    				}
    				//case7 end
    				//case8 w的左孩子是红色
    				if (w->left->color == RED)
    					w->left->color = BLACK;
    				w->color = parent_x->color;
    				parent_x->color = BLACK;	
    				root = right_rotate(root, parent_x);
    				x = root;
    				//case8 end
    			}
    		}
    	}
    
    	if(x != NULL)
    		x->color = BLACK;
    	return root;
    }
    
    treeNode* rb_delete(treeNode *root, int value)
    {
    	treeNode *z, *y, *x;
    	z = root;
    	while(z != NULL)
    	{
    		if(z->key == value)
    			break;
    		if(z->key < value)
    			z = z->right;
    		else
    			z = z->left;
    	}
    	if(z == NULL) //木有找到要删除的节点
    		return root;
    	y = z;
    	if(y->left!=NULL && y->right!=NULL)//两个孩子的情况,y是z的中序后继
    	{
    		y = y->right;
    		while (y->left != NULL)
    		{
    			y = y->left;
    		}
    		z->key = y->key;
    	}
    	//x是y唯一的孩子或者NULL
    	if(y->left != NULL)
    		x = y->left;
    	else x = y->right;
    	
    	if(x)
    		x->p = y->p;
    	if(y->p == NULL)//y是根节点
    		root = x;
    	else if(y == y->p->left)
    		y->p->left = x;
    	else y->p->right = x;
    	
    	if(y->color==BLACK)
    		root = rb_delete_fixup(root, x, y->p);
    	delete y;
    	return root;
    }
    
    treeNode* rb_find(treeNode *root, int value)
    {
    	treeNode *x = root;
    	int h = 0;
    	while (x != NULL)
    	{
    		if (x->key == value)
    		{
    			if(x->color == RED)
    				printf("RED ");
    			else
    				printf("BLACK ");
    			printf("从0层计数,%d在第%d层
    ", value, h);
    			return x;
    		}
    		if (x->key < value)
    			x = x->right;
    		else x = x->left;
    		h++;
    	}
    	return NULL;
    }
    
    void midOrder(treeNode *root)
    {
    	if(root == NULL)
    		return;
    	midOrder(root->left);
    	printf("%d-", root->key);
    	if(root->color == RED)
    		printf("RED ");
    	else
    		printf("BLACK ");
    	midOrder(root->right);
    }
    
    void preOrder(treeNode *root)
    {
    	if(root == NULL)
    		return;
    	printf("%d-", root->key);
    	if(root->color == RED)
    		printf("RED ");
    	else
    		printf("BLACK ");
    	preOrder(root->left);
    	preOrder(root->right);
    }
    
    
    int main()
    {
    	int select, a;
    	treeNode *root = NULL;
    	do 
    	{
    		printf("请输入选择 1--插入,2--查询, 3--删除, 4--输出中序和前序,其他跳出
    ");
    		scanf("%d", &select);
    		if (select == 1)
    		{
    			printf("输入将要插入的值, -1表示结束
    ");
    			while(scanf("%d", &a), a != -1)
    			{
    				root = rb_insert(root, a);
    			}
    		}
    		else if (select == 2)
    		{
    			printf("输入将要查询的值, -1表示退出查询
    ");
    			while(scanf("%d", &a), a != -1)
    			{
    				if(rb_find(root, a) != NULL)
    				{
    					//printf("找到了
    ");
    				}
    				else
    					printf("木有找到
    ");
    			}
    		}
    		else if(select == 3)
    		{
    			printf("输入将要删除的值, -1表示退出删除
    ");
    			while(scanf("%d", &a), a != -1)
    			{
    				root = rb_delete(root, a);
    			}
    		}
    		else if(select == 4)
    		{
    			printf("中序是:
    ");
    			midOrder(root);
    			printf("
    前序是:
    ");
    			preOrder(root);
    			printf("
    ");
    		}
    		else break;
    
    	} while (true);
    	
    
    	getchar();
    	return 0;
    }
    

    运行结果,据小雨自己测试没有错误,欢迎大家前来拍砖

  • 相关阅读:
    mongoDB常用命令
    Linux下安装MongoDB
    Linux下mysq基础命令(二)
    Linux下mysql基础命令(一)
    Linux 下使用yum 命令安装MySQL
    Linux 常用命令
    windows7用WMware安装Linux虚拟机详细步骤
    接口测试全流程扫盲
    Jmeter时间格式化
    Jmeter之测试报告
  • 原文地址:https://www.cnblogs.com/rain-lei/p/3583185.html
Copyright © 2011-2022 走看看