前言:在做题过程中发现,对于二叉查找树删除操作,在许多blog的代码实现中多为函数赋值调用。但作者发现删除过程中这会造成过大的内存浪费,因此想到了用函数调用的引用传递。
删除的实现方式各大blog均已详细说明,本文不再赘述。
本文着重分析删除函数。
附代码及注释:
#include<bits/stdc++.h>
using namespace std;
typedef struct node{
int data;
struct node *ll;
struct node *rr;
}bte;
///插入函数
void insert( bte *&root, int x ){
if( root == NULL ){
root = new bte;
root->data = x;
root->ll = NULL;
root->rr = NULL;
}else{
if( x < root->data )insert( root->ll, x );
else if( x > root->data )insert( root->rr, x );
}
}
///查找函数
void find( bte *&root, int x ){
bte *p = root;
while( p ){
if( x < p->data )
p = p->ll;
else if( x > p->data )
p = p->rr;
else
break;
}
if( p )printf("yes\n");
else printf("no\n");
}
///删除函数
void del( bte *&root, int x ){///因为用到引用传递,因此无需返回值。用void类型。
if( x < root->data ){ ///往左子树递归寻找
if( root->ll )del( root->ll , x );
}else if( x > root->data ){ ///往右子树递归寻找
if( root->rr )del( root->rr, x );
}else{ ///找到要删除的节点
if( root->rr && root->ll ){ ///有左右子树
bte *pp = root->rr;
while( pp->ll ) ///遍历找出右子树最小值
pp = pp->ll;
root->data = pp->data; ///右子树中代替节点赋值给删除节点
del( root->rr , pp->data ); ///递归找出代替节点并删除
}else{ ///只有单子树或无子树。
///该行以下操作才是真正的删除操作,以上操作均为删除做寻找,做准备
///以下前两操作均为删除当前的节点root,root的子节点代替root
if( !root->ll ){
bte *p = root;
root=root->rr;
free(p);
}else if( !root->rr ){
bte *p = root;
root=root->ll;
free(p);
}else if(!root->rr&&!root->ll)free(root); ///直接释放
}
}
}
int main(){
int n;
scanf("%d",&n);
bte *root = NULL;
while( n-- ){
int x;
char op[20];
char a[20] = {"insert"};
char b[20] = {"find"};
char c[20] = {"delete"};
scanf("%s",op);
scanf("%d",&x);
if( !strcmp(op,a) ){
insert( root, x );
}else if( !strcmp(op,b) ){
find( root, x );
}else if( !strcmp(op,c) ){
del( root, x );
}
}
destroy( root );
}
总结:
删除操作实质就是用其他合适的节点来代替当前要删除的节点,代替操作完成会造成节点关系之间的改变,即地址之间的联系会改变。因此方法1(函数赋值调用)可以调用结构类型的函数返回删除节点所在树的根节点使得二叉查找树保持关系,但不断地调用结构类型函数,会造成需要大量的空间。而方法2(函数引用传递)则可以通过引用传递直接对要删除的节点操作。