红黑树-详细剖析-如果你有一天或者两天的时间,借助此文,能够学会红黑树的。

看了算法导论,也看了wikipedia,纠结了好几天,终于能看懂红黑树了。于是就写下这边博客,以供自己以后参考,或者能够和大家交流,希望能够共同学习。红黑树是很复杂的一种树。它的典型用途就是关联数组。

至于红黑树的删除,必定会给出耳目一新的思路。

一,红黑树的插入

按照常理,给出红黑树的性质:

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

2,根节点是黑色的。

3,每个NIL节点都是黑色的。

4,如果一个节点是红色的,那么它的两个子节点都是黑色的。

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

对于性质5,做一下解释,性质5所叙述的有些模糊,我觉得可以这样说:对于每个节点,从此节点开始,包括此节点,到其所有叶子节点的路径上的黑节点的个数相同,不应该说是子孙节点。还有要谨记第4条,这条的额外之意就是不能有两个连续节点的颜色为红色。

接下来解析旋转,即左旋或者右旋。先加以解释,然后在再给出伪代码和图。可以这样理解,既然决定进行旋转,那么旋转点x的左子节点或者右子节点就必然会存在,要不然旋转就会失去意义。旋转的实质是什么:对于左旋,就是旋点x下降到x的右孩子的位置,而x的左孩子上升到x之前的位置,然后把x的左孩子的右孩子连接到x的左孩子,x成为x之前左孩子的右孩子。如果觉得绕的话可以画下图。对于右旋与左旋相反。总之就记住两个关键字:下降和上升。加上图,我相信大家能够很好的理解旋转的意思。至于为什么会应用左旋或者右旋,那都是由于性质4,5导致的。那么可能会有疑问,通过旋转就可以使改变后的红黑树达到原来的状态吗?不是的,还要重新对相关的节点着色。到这里只需要知道,旋转是为了使红黑树保持上述的5个性质。接下来的问题就是,为什么红黑树这么特殊,要求有这么多。答案就是,经过硬性的规定这5条性质,可以达到这个要求:从根到叶子节点的最常的路径不多余最短路径的两倍。因此比如插入或者删除操作和查找某个值在最坏的情况下,也就是在经过最长路径的情况下,时间都与红黑树的高度成正比。至于非要追究为什么要有这5条性质,我想应该是红黑树的创建者们经常长时间的探索总结出来的。

左旋:

LEFT-ROTATE(T, x)  
1  y ← right[x] ▹ Set y.  
2  right[x] ← left[y]      ▹ Turn y's left subtree into x's right subtree.  
3  p[left[y]] ← x  
4  p[y] ← p[x]             ▹ Link x's parent to y.  
5  if p[x] = nil[T]  
6     then root[T] ← y  
7     else if x = left[p[x]]  
8             then left[p[x]] ← y  
9             else right[p[x]] ← y  
10  left[y] ← x             ▹ Put x on y's left.  
11  p[x] ← y  


右旋:

RB-INSERT(T, z)  
 1  y ← nil[T]  
 2  x ← root[T]  
 3  while x ≠ nil[T]  
 4      do y ← x  
 5         if key[z] < key[x]  
 6            then x ← left[x]  
 7            else x ← right[x]  
 8  p[z] ← y  
 9  if y = nil[T]  
10     then root[T] ← z  
11     else if key[z] < key[y]  
12             then left[y] ← z  
13             else right[y] ← z  
14  left[z] ← nil[T]  
15  right[z] ← nil[T]  
16  color[z] ← RED  
17  RB-INSERT-FIXUP(T, z) 


接下来将给出红黑树插入的伪代码和给出红黑树调整的伪代码,然后给出八个实例图,通过这8个实例图的分析,我想大家能够熟悉红黑树插入过程中遇到的6种情况。

以下称6-11为case1,14-22为case2,20-22为case3,27-33为case4,37-44为case5,42-44为case6。如果有看过算法导论的,就会觉得和算法导论有出入,分别是case2和case5,为什么呢,因为case2和case3有代码重叠的部分。case2和case5都是双旋。等下在下面的例子中大家就会看到。

插入:

RB-INSERT(T, z)  
 1  y ← nil[T]  
 2  x ← root[T]  
 3  while x ≠ nil[T]  
 4      do y ← x  
 5         if key[z] < key[x]  
 6            then x ← left[x]  
 7            else x ← right[x]  
 8  p[z] ← y  
 9  if y = nil[T]  
10     then root[T] ← z  
11     else if key[z] < key[y]  
12             then left[y] ← z  
13             else right[y] ← z  
14  left[z] ← nil[T]  
15  right[z] ← nil[T]  
16  color[z] ← RED  
17  RB-INSERT-FIXUP(T, z)  

旋转:

 1while color[p[z]] == RED 
 2{ 
 3    if( p[z] == left[p[p[z]]])  
 4	  {
 5		  y ← right[p[p[z]]]  
 6	         if color[y] == RED
 7	         {
 8		   color[p[z]] ← BLACK                     
 9                 color[y] ← BLACK                         
10                 color[p[p[z]]] ← RED                     
11                 z ← p[p[z]]
12	          }
13		  else 
14		  {		 
15	            if (z == right[p[z]])
16		    {
17			 z ← p[z]                         
18                      LEFT-ROTATE(T, z)  
19		    }
20                  color[p[z]] ← BLACK                  
21                  color[p[p[z]]] ← RED                 
22                  RIGHT-ROTATE(T, p[p[z]])   
23		}
24	  }
25      else  
26	  {
27		  y ← left[p[p[z]]]  
28	          if color[y] == RED
29	          {
30		    color[p[z]] ← BLACK                     
31                  color[y] ← BLACK                         
32                  color[p[p[z]]] ← RED                     
33                  z ← p[p[z]]
34		   }
35	          else 
36	         {		 
37		    if (z == left[p[z]])
38		    {
39			 z ← p[z]                         
40                      RIGHT-ROTATE(T, z)  
41		     }
42                  color[p[z]] ← BLACK                  
43                  color[p[p[z]]] ← RED                 
44                  LEFT-ROTATE(T, p[p[z]])   
45		 }
46	  }
47 }
48 color[root[T]] ← BLACK  

实例一:注意节点7是黑色的,大家画图的时候要画成黑色的,我就不修改了。


从图中可以看到新插入的节点4和其父节点5,都是红色的违反了红黑树的性质4,同时注意到4的父亲节点的兄弟8是红色的,所以图中的条件满足case1,那么解决的办法是把x的父亲节点和它的叔叔节点都变成黑色的,然后把他们的祖父的颜色变成红色的。改变x的指向,指向其祖父节点。到下图:


这是调整后的状态,x指向7,条件符合case2,先把x重新指向,继而需要进行双旋,即先左旋,后右旋,下面给出这两部的图,注意,左旋后,要改变颜色的。



这就是插入节点4后,红黑树重新调整的过程,共经过了case1和case2,。

实例二:这次只给出图形,然后由大家自己画图分析,看是哪种情况。


实例三:


这种情况很容易看出就是case1,然后进行处理,得到如下的图:


分析一下后得知,当x指向7的时候,没有通过case2的测试,所以这个状态属于case3,对7的父节点重新着色为黑,祖父节点着色为红,然后以祖父节点为旋点,进行右旋后,红黑树就会转为如下的平衡状态:


实例四:同样只给出例图,然后由大家自己分析:


实例五:


同样可以分析出这张图的状态属于case1,然后进行调整后得到如下的图:


那么,大家看一下,上面这张图应该属于第几种情况呢,应该属于第5中情况了,所以需要进行双旋,旋点为14,先进行右旋:


右旋之后入上图,此时的x应该指向14,然后继续执行程序,将x的父节点着色成黑,祖父节点着色成红,然后以祖父节点为旋点进行左旋:


实例六:给出图形,大家尝试分析


实例七:


实例八:


经过这八个实例的分析,结合伪代码,我想肯定能够对红黑树的插入有少许的理解,最重要的是动手画图,在画图过程中就会逐步理解红黑树。

二,红黑树的删除

红黑树属于二叉查找树的一种,因此红黑树的删除类似于二叉查找树的删除。同时,又由于红黑树的特殊性质,删除节点之后要进行颜色的调整,所以情况就变得复杂了。建议在学习红黑树的删除时先看懂二叉查找树的删除情况。

先给出红黑树的删除的伪代码:

1 if left[z] = nil[T] or right[z] = nil[T]  
 2    then y ← z  
 3    else y ← TREE-SUCCESSOR(z)  
 4 if left[y] ≠ nil[T]  
 5    then x ← left[y]  
 6    else x ← right[y]  
 7 p[x] ← p[y]  
 8 if p[y] = nil[T]  
 9    then root[T] ← x  
10    else if y = left[p[y]]  
11            then left[p[y]] ← x  
12            else right[p[y]] ← x  
13 if y ≠ z  
14    then key[z] ← key[y]  
15         copy y's satellite data into z  
16 if color[y] = BLACK  
17    then RB-DELETE-FIXUP(T, x)  
18 return y  
可以清晰的看出红黑树的删除和二叉查找树的删除除了16,17行不相同之外,其他基本一致。(伪代码来自算法导论)。

删除中应该考虑三种情况,如上述算法的1-3行所示:所删除节点的左孩子节点为空,所删除节点的右孩子为空,所删除节点的两个孩子节点都不为空。前两种情况的删除可以通过在z节点的父节点和z节点的不空的子节点之间建立一条链来删除z。第三种情况,如果z的左右子节点都不为空,则删除z的直接后继y,然后用y的内容来代替z的内容,其实直接钱驱也可以,看自己怎么选择。这个方法在数据结构书上应该看到过。着重讨论删除节点后红黑树的颜色的调整。如果所删除的节点的颜色是红色的,那么对红黑树的性质没有影响,为什么呢,理由有如下三点:红黑树中各节点的黑高度没有变化,也就是说,红黑树的第五个性质:对于每个节点,从此节点开始,到其所有叶子节点所经过的路径上的黑节点个数没有变化,是相同的。第二,不存在相邻的红节点。根据性质4:如果一个节点是红色的,那么它的两个子节点都是黑色的。这个红色节点的父节点必定是黑色的,如果删除这个红色的节点。那么是两个黑节点相连,所以没有连续的两个红色节点。第三,因为删除的节点是红色的,所以它就不可能是根节点,如果是根节点,那么红黑树在删除节点之前性质就是没有按照5个性质的约定,是错误的。

下面讨论,如果删除的节点是黑色的节点,给出红黑树颜色调整的例子:

 1 while x ≠ root[T] and color[x] = BLACK  
 2     do if x = left[p[x]]  
 3           then w ← right[p[x]]  
 4                if color[w] = RED  
 5                   then color[w] ← BLACK                        ▹  Case 1  
 6                        color[p[x]] ← RED                       ▹  Case 1  
 7                        LEFT-ROTATE(T, p[x])                     ▹  Case 1  
 8                        w ← right[p[x]]                         ▹  Case 1  
 9                if color[left[w]] = BLACK and color[right[w]] = BLACK  
10                   then color[w] ← RED                          ▹  Case 2  
11                        x p[x]                                   ▹  Case 2  
12                   else if color[right[w]] = BLACK  
13                           then color[left[w]] ← BLACK          ▹  Case 3  
14                                color[w] ← RED                  ▹  Case 3  
15                                RIGHT-ROTATE(T, w)               ▹  Case 3  
16                                w ← right[p[x]]                 ▹  Case 3  
17                         color[w] ← color[p[x]]                 ▹  Case 4  
18                         color[p[x]] ← BLACK                    ▹  Case 4  
19                         color[right[w]] ← BLACK                ▹  Case 4  
20                         LEFT-ROTATE(T, p[x])                    ▹  Case 4  
21                         x ← root[T]                            ▹  Case 4  
22        else (same as then clause with "right" and "left" exchanged)  
23 color[x] ← BLACK  

1,如果删除的节点是根节点。分两种情况,如果根节点的左右子树中其中一个为空,想一下,红黑树的性质,最长的路径不超过最短路径的两倍。一个为空了,那么另一个子树的深度不会超过2,那么直接让它的左孩子或者右孩子代替它,不管怎么样,让新的根的颜色为黑色就好了。这样整个树种剩下的节点个数不超过2个了。另一种情况就是左右子树都不为空,那么就应该慎重了,再一下的分析中会给出答案。

2,第二种情况就是如果删除的这个节点的左右子树都不为空,那么就找到这个节点的直接后继,就是右子树中最左边的节点,称这个节点为x。那么x肯定没有左孩子,为什么呢,因为如果它有左孩子,它就不是最左边的节点。我们要讨论的是它有没有右孩子,如果没有,直接删除这个节点就好了,颜色什么的都不用调整。原因是删除它,虽然是黑色的节点,但是仍旧没有违反红黑树的性质。第一,没有违反性质2,没有违反性质4,那性质5呢,更没有。仔细分析一下性质5,对于每个节点,从此节点开始,到其叶子节点的路径上的黑节点个数都一样,注意,要有路径的。如果x连有孩子都没有,删除它,从此x父节点的左子树就为空了,没有了路径,怎么会有红黑节点呢。另外一种情况就是x有右子树。

3,着重讨论一般情况下的删除,删除调整树节点的颜色一共有8中情况,但是其中的四种和另外四种是对称的,所以理解了前四种,后四种就可以清楚了。读下伪代码就可以知道,这两组4个的情况是根据调整节点x是其父节点的左孩子还是右孩子来区分的。

先给出图,然后我们做一些假设,并依据这些假设来一步一步的调整红黑树的颜色。


我们假设A为删除节点的子节点,那么删除后及诶单调整后A的父亲为W,同时假设a,b,c,d,m,n为6棵不同的子树。根绝红黑树的性质,在不删除A的父节点之前,这个子树的叶子节点,到根节点W的所有路径中的黑节点个数是一样的。从图中可以看出,因为删除A的父节点是黑色的,所以a,b中含有的黑节点个数要比c,d,m,n中的黑节点个数少一个,故做如下的假设。


继续分析上图,可以看到,A的兄弟节点B,是红色的,注意,在这种情况下A的父节点的颜色必须是黑色的。因为红黑树中,不准许出现两个连续红色的节点。同时因为B是红色的,它的两个子节点C,D都是黑色的。那么删除A的父节点,碰到这种情况应该怎么做呢:解决办法,如伪代码所示意的,将A的兄弟B的颜色调整为黑色,A的父亲节点的颜色调整为红色。然后以W为旋点,进行左旋,得到如下的图:


此时在统计一下,a,b,c,d,m,n中叶子节点到根节点B的黑节点个数


可以看到,这次调整的目的没有达到。红黑树的性质5还是没有被满足的。那么接下来该怎么办呢。就是讨论A节点的兄弟节点C的孩子节点的颜色。给出如下图:

这是第一种情况,就是C节点的左右子节点的颜色都是黑色的。


重新调整后,出现了8棵小子树,就当从c中拿出其根节点,d中拿出其根节点F,统计一下,这个8个子树中最低端叶子节点到B的黑节点的个数。

在上图中A的兄弟节点C是黑色的,其左右子节点E,F都是黑色的,这种情况怎么办呢。让A的兄弟节点C的颜色变为红色,这样以后就成了如下图的情况:


图中出现了连续两个红节点的情况,解决的办法是什么呢:就是按照伪代码中所示的,把x节点重新指向A的父亲W,然后呢,根据while循环的条件可以得出,循环终止,将当前x的颜色着色为黑色。便有如下图:


然后再做一下统计:


这样红黑树就平衡了。

再分析下一种情况,就是A的兄弟节点C的右孩子是黑色,而左孩子是红色的情况。这种情况涉及双旋。

各个子树到B的黑节点个数为:


这种情况的解决办法是交换C节点E节点的颜色,然后以C节点为旋点进行右旋,得到如下的图(此时x节点指向A,w节点指向C)。


旋转后,统计一下各个子树到根节点B的黑节点个数,看此时的红黑树是不是平衡的。


可以看出,此时的红黑树是不平衡的,那么应该怎么解决呢。根据算法的伪代码,我们在这次的调整过程中,已经让w重新指向E。接下来对上图的做法就是,将A节点父节点的颜色转到w节点,然后将w节点即E节点的右孩子变成黑色的。w节点的父节点变成黑色的,然后以w节点E为旋点进行左旋。得到如下的图形:


然后我们再统计一下,各个子树到根节点B的黑节点个数:


一样多了,那么说明我们经过调整,红黑树在删除节点之后重新平衡了。

其他情况大家可以自己分析。

三,红黑树的c代码

#include<stdio.h>
#include<stdlib.h>
#define BLACK  0
#define RED 1
typedef struct rb_tree_node{
	int key;
	struct rb_tree_node *parent;
	struct rb_tree_node *left;
	struct rb_tree_node *right;
	int color;
}RBTNode, *RBTree;
void left_rotate(RBTree *T, RBTNode *cur_node) {
	RBTNode *rchild_node; // rigth child node of current node

	rchild_node = cur_node->right;  
	cur_node->right = rchild_node->left;
	if(cur_node->right != NULL){
		(cur_node->right)->parent = cur_node;
	}

	rchild_node->parent = cur_node->parent;
	if(cur_node->parent == NULL){
		*T = rchild_node;
	} else {
		if(cur_node == (cur_node->parent)->left)
			(cur_node->parent)->left = rchild_node;
		else
			(cur_node->parent)->right = rchild_node;
	}

	cur_node->parent = rchild_node; 
	rchild_node->left = cur_node; // cur_node to be left child
}
void right_rotate(RBTree *T, RBTNode *cur_node) {
	RBTNode *lchild_node;

	lchild_node = cur_node->left;
	cur_node->left = lchild_node->right;
	if(cur_node->left != NULL) {
		(cur_node->left)->parent = cur_node;
	}

	lchild_node->parent = cur_node->parent;
	if(cur_node->parent == NULL) {
		*T = lchild_node;
	} else {
		if(cur_node == (cur_node->parent)->left)
			(cur_node->parent)->left = lchild_node;
		else
			(cur_node->parent)->right = lchild_node;
	}

	cur_node->parent = lchild_node;
	lchild_node->right = cur_node; // cur_node to be left child of lchild_node
}
void rb_tree_insert_fixup(RBTree *T, RBTNode *cur_node) {
	RBTNode *parent;
	RBTNode *grandparent;
	RBTNode *uncle;

	while((parent = cur_node->parent) != NULL && parent->color == RED){

		grandparent = parent->parent;
		if(grandparent == NULL)
			break;

		if(parent == grandparent->left) {
			// brother of node's parent 
			uncle = grandparent->right;

			if(uncle != NULL && uncle->color == RED) {//case 1
				uncle->color = BLACK;
				parent->color = BLACK;
				grandparent->color = RED;
				cur_node = grandparent;

			} else {//case 2,3
				if(cur_node == parent->right) {
					cur_node = parent;
					left_rotate(T, cur_node);
				}
				//case 3
				parent = cur_node->parent;
				parent->color = BLACK;
				grandparent->color = RED;
				right_rotate(T, grandparent);
			}

		} else {
			uncle = grandparent->left;
			if(uncle != NULL && uncle->color == RED) {//case 4
				uncle->color = BLACK;
				parent->color = BLACK;
				grandparent->color =RED;
				cur_node = grandparent;
			} else {//case 5 ,6
				if(cur_node == parent->left) {
					cur_node = parent;
					right_rotate(T, cur_node);
				}
				//case 6
				parent = cur_node->parent;
				parent->color = BLACK;
				grandparent->color = RED;
				left_rotate(T, grandparent);
			}
		}
	}
	(*T)->color = BLACK;
}
int rb_tree_search_auxiliary(RBTree T, RBTNode *parent, RBTNode **cur_node, int key) {
	if(!T){
		*cur_node = parent;
		return 0;
	}
	if(key == T->key) {
		*cur_node = T;
		return 1;
	} else if(key < T->key){
		return rb_tree_search_auxiliary(T->left, T, cur_node, key);
	} else {
		return rb_tree_search_auxiliary(T->right, T, cur_node, key);
	}
}
void rb_tree_insert(RBTree *T, int key) {
	RBTNode *p;
	if(!rb_tree_search_auxiliary(*T, NULL, &p, key)) {
		RBTNode *s = (RBTNode*)malloc(sizeof(RBTNode));
		s->key = key;
		s->left = NULL;
		s->right = NULL;
		s->parent = NULL;
		s->color = RED;

		if(!(*T)) {
			(*T) = s;
		} else {
			if(key < p->key) {
				p->left = s;
			} else {
				p->right = s;
			}
			s->parent = p;
		}
		rb_tree_insert_fixup(T, s);
	}
}
int rb_tree_search(RBTree T, int key) {
	RBTNode *p;
	int result = 0;
	result = rb_tree_search_auxiliary(T, NULL, &p, key);
	return result;
	free(p);
}

void rb_tree_delete_fixup(RBTree *T, RBTNode *cur_node) {
	RBTNode *parent = NULL;
	RBTNode *uncle = NULL;
	while((cur_node != NULL) && (cur_node != *T) && (cur_node->color == BLACK)) {
		parent = cur_node->parent;
		if(cur_node == parent->left) {
			uncle = parent->right;
			if(uncle->color == RED) {
				uncle->color = BLACK;
				parent->color = RED;
				left_rotate(T, parent);
				uncle = cur_node->parent->right;
			}
			if(uncle->left->color == BLACK && uncle->right->color == BLACK) {
				uncle->color = RED;
				cur_node = cur_node->parent;
			} else{
				if(uncle->right->color == BLACK) {
					uncle->left->color = BLACK;
					uncle->color = RED;
					right_rotate(T, uncle);
					uncle = cur_node->right;
				}
				uncle->color = uncle->parent->color;
				uncle->right->color = BLACK;
				uncle->parent->color = BLACK;
				left_rotate(T, uncle->parent);
				cur_node = *T;
			}
		} else {
			uncle = parent->left;
			if(uncle->color == RED) {
				parent->color = RED;
				uncle->color = BLACK;
				right_rotate(T, parent);
				uncle = cur_node->parent->left;
			}
			if(uncle->left->color == BLACK && uncle->right->color == BLACK) {
				uncle->color = RED;
				cur_node = parent;
			} else{ 
				if(uncle->left->color == BLACK) {
					uncle->right->color = BLACK;
					uncle->color = RED;
					left_rotate(T, uncle);
					uncle = parent->left;
				}
				uncle->color = parent->color;
				parent->color = BLACK;
				uncle->left->color = BLACK;
				right_rotate(T, uncle->parent);
				cur_node = *T;
			}
		}
	}
	if(cur_node != NULL)
		cur_node->color = BLACK;
}

RBTNode* rb_tree_delete_auxiliary(RBTree *T, RBTNode *old_node) {
	RBTNode* child_node = NULL;// 要删除节点的孩子节点
	RBTNode* deleting_node = NULL; //要删除的节点
	if(old_node->left == NULL || old_node->right == NULL){ //有一个为空,或者全为空
		deleting_node = old_node;
	} else {//两个子节点都不为空,则
		deleting_node = old_node->right;
		while(deleting_node->left != NULL)
			deleting_node = deleting_node->left;
	}

	if(deleting_node->left != NULL)
		child_node = deleting_node->left;
	else
		child_node = deleting_node->right;

	if(child_node != NULL) {//孩子节点全为空的情况
		child_node->parent = deleting_node->parent;
	}
	if(child_node != NULL && child_node->parent == NULL){//说明删掉的是根节点
		*T = child_node;
	} else {
		if(deleting_node == (deleting_node->parent)->left)
			(deleting_node->parent)->left = child_node;
		else
			(deleting_node->parent)->right = child_node;
	}

	if(deleting_node != old_node) {
		old_node->key = deleting_node->key;
	}
	if(deleting_node->color == BLACK){
		rb_tree_delete_fixup(T, child_node);
	}
	return deleting_node;
}
int rb_tree_delete(RBTree *T, int key) {
	RBTNode *p;
    RBTNode *rst = NULL;
	int result = 0;
	result = rb_tree_search_auxiliary(*T, NULL, &p, key);
	if(!result){//要删除的目标节点不存在,删除失败
		return 0;
	} else {
		rst = rb_tree_delete_auxiliary(T, p);
		if(rst){
			free(rst);
			return 1;
		}else{
			return 0;
		}
	}
}
void pre_order_traverse(RBTree T) {
	if(T) {
		printf("key=%d,color=%d\n", T->key, T->color);
		pre_order_traverse(T->left);
		pre_order_traverse(T->right);
	}
}
void main() {
	RBTree T = NULL;
	rb_tree_insert(&T, 41);
	rb_tree_insert(&T, 38);
	rb_tree_insert(&T, 31);
	rb_tree_insert(&T, 12);
	rb_tree_insert(&T, 19);
	rb_tree_insert(&T, 8);
	pre_order_traverse(T);
	if(rb_tree_search(T, 19))
		printf("19 exist!\n");
	if(rb_tree_delete(&T,38))
		printf("delete 38 ok!\n");
	printf("root=%d\n",T->key);
	pre_order_traverse(T);
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值