user-0 提问: 求助求助,二叉排序树删除结点的问题

回复:
user: user-0
content: 大哥,您太好了 这是辛苦了

user: user-1
content: 看别人的代码辛苦，不如我们来说说这个事应该或者说可以怎么做。  首先，假定我们把我们要做的函数的原型声明成这样  node * remove_node_with_value(node * tree， int value);   它干什么呢，给定一颗输入的binary search tree, 和特定的值，从树中删除值为给定值的节点，但要保证其他剩余的节点还是构成一株binary search tree，返回这颗新树的根节点。这个函数怎么写呢？树的特点就是递归，如果value比根节点的value小，从其左子树中删之，如果value比根节点的value大，从其右子树删之。难点是当要删除的节点就是根节点的时候。先易后难，这个难点我们先保留，好了代码出来了  node * remove_node_with_value(node * tree， int value) {      if(tree)      {           if(value<tree->value)                tree=remove_node_with_value(tree->left, value);           else if(value>tree->value)                tree=remove_node_with_value(tree->right, value);           else                tree=remove_root_node(tree);      }      return tree; }   好了，在我们把重心转向我们新的难点remove_root_node之前，我们来想想上面的方法可能的漏洞。binary search tree一般是允许节点的值重复的，它的构造方式一般是如果比根节点的值小，加在左树，反之加在右树。也就是说当要删除的值在根节点时，不能排除右子树还有同值的节点，这个节点该不该删除了？这是需求定义的，如果需要，上面的代码就该改成这样  node * remove_node_with_value(node * tree， int value) {      if(tree)      {           if(value<tree->value)                tree=remove_node_with_value(tree->left, value);           else{                if(tree->value==value)                    tree=remove_root_node(tree);                               tree=remove_node_with_value(tree->right, value);          }      }      return tree; }   好了注意力转回remove_root_node, 我们需要这个函数做的事是，对给定的排序二叉树，删除其根节点，但要保证其余的节点构成一株排序二叉树。同样的，返回值是新的树的根节点(指向其的指针)。  分三种情形，该节点的左子树为空，直接返回右子树就好；该节点的左子树为空，直接返回左子树就好；第三种左右子树都非空，这个是难点，先易后难，好了代码出来了  /* param: tree should not be empty */ node * remove_root_node(node * tree) {       node * n;       if(!tree->left)           n=tree->left;       else if(!tree->right)           n=tree->right;       else{ // both left and right are non-empty         // to be continued ...       }       free(tree);       return n; }   如果一个树的根节点的左右子树都非空，怎样搬走它呢？我们知道，所有左子树的节点的值都小于根节点的值，从而也都小于右子树的节点的值，如果我们能把左子树节点中值最大的节点提拔上来做新树的根节点，可以直接把原右子树做为这个节点的右子树，新树构成一株去掉了原根节点后的新的排序二叉树，而这这是我们要的。现在问题有转化成给定一株树，如何廉价地从它构造出一颗新的排序二叉树，该树包含了原树的所有节点，但根节点的右子树为空。在具体实现这个函数时，我们先给他起个名字，定个原型  node * reform_tree_to_null_right_tree(node * tree);   有了它，上面的函数就可以完满了。如下  /* param: tree should not be empty */ node * remove_root_node(node * tree) {       node * n;       if(!tree->left)           n=tree->left;       else if(!tree->right)           n=tree->right;       else{ // both left and right are non-empty           n=reform_tree_to_null_right_tree(tree->left);           n->right=tree->right;       }       free(tree);       return n; }   转战reform_tree_to_null_right_tree。树的最大节点是最右边的那个节点。这个节点必须被提拔到最高层。代码如下:   node * reform_tree_to_null_right_tree(node * tree) { 	node * n, *m; 	if(tree->right==NULL) 		return tree; 	n=tree; 	while(n->right) 	{ 		m=n; 		n=n->right; 	} 	m->right=n->left; 	n->left=tree; 	return n; }   什么意思呢？如果根节点的右子树本来就为空，共产主义已经实现，还折腾啥？直接返回吧！否则向右再向右，直到行到水穷处，好了这个家伙就是最大个的了。我们需要记住它的父节点，把这个节点的左子树交给父节点做右子树就好了(为什么？）  到这里就完成了。程序不重要，甚至找出你的错误不重要。这里展示的一种分而治之，自顶向下设计的思维方式，是面向过程的C程序员必须具备并能熟练运用的。这个技能到C++或者其他语言的时候，也还是继续有效的。  下面是一个简单测试过的完整实现:  #include <stdio.h> #include <stdlib.h>  typedef struct node{ 	int value; 	struct node *left, *right; }node;  node* binary_tree_add(node * tree, int value) { 	if(!tree) 	{ 		node * n; 		n=(node*)malloc(sizeof(node)); 		n->value=value; 		n->left=n->right=NULL; 		return n; 	} 	if(value<tree->value) 		tree->left=binary_tree_add(tree->left, value); 	else 		tree->right=binary_tree_add(tree->right, value); 	 	return tree; }   void left_root_right(node * n) { 	if(!n) 		return; 	left_root_right(n->left); 	printf("%d ", n->value); 	left_root_right(n->right); }  void print_tree(node *n) { 	left_root_right(n); 	putchar('\n'); }   /* remove the root node of a tree  * and return the pointer to the root of the new binary search   * tree with the original rood node removed  *   * param: tree should not be empty  */ node * remove_root(node * tree);  node * delete_node_with_value(node * tree, int value) { 	if(tree) 	{ 		if(value<tree->value) 			tree->left=delete_node_with_value(tree->left, value); 		else{ 			if(tree->value==value) 				tree=remove_root(tree); 			if(tree && tree->right) 				tree->right=delete_node_with_value(tree->right, value); 		} 	} 	return tree; }  /* for input binary search tree t, build a new binary search tree such that  * the right child of the new tree is NULL.   *  * param: tree should not be NULL  */  node * reform_tree_to_null_right(node * tree);  node * remove_root(node * tree) { 	node * n; 	if(tree->left==NULL) 		n=tree->right; 	else if(tree->right==NULL) 		n=tree->left; 	else{ 		n=reform_tree_to_null_right(tree->left); 		n->right=tree->right; 	} 	free(tree); 	return n;		 }  node * reform_tree_to_null_right(node * tree) { 	node * n, *m; 	if(tree->right==NULL) 		return tree; 	n=tree;  	while(n->right) 	{ 		m=n; 		n=n->right; 	} 	m->right=n->left; 	n->left=tree; 	return n; }   int main(int argc, const char *argv[]) { 	 	 	node * tree=NULL; 	 	srand(time(NULL)); 	 	for(int i=0; i<20; ++i) 		tree=binary_tree_add(tree,rand()%100); 	tree=binary_tree_add(tree,50); 	 	print_tree(tree);  	tree=delete_node_with_value(tree,50);  	print_tree(tree);  	return 0; }