二叉搜索树的删除操作
题目链接:http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=ALDS1_8_C
比之前面的两题,再加上删除的操作。
现在假设已经有了一颗二叉搜索树,然后我想要删除一个节点z;那么有以下三种情况
case 1: z没有子树,即z为叶子节点
case 2: z只有一颗子树,即只有左子树或只有右子树
case 3: z既有左子树又有右子树
对于case1是最简单的,直接把z->parent节点的儿子节点赋NIL就可以了;
对于case2,只要把z->parent的儿子赋为z的子树,把z的子树的parent节点赋为z->parent即可;
对于case3,比较复杂,因为BST满足左子树所有值都小于根节点;右子树所有值都大于根节点。所以我们需要找到z节点的 左子树中的最大值 或者 右子树中的最小值。也就是左子树中的最右节点 或者 右子树中的最小节点。比如说x;然后我们只要把x删除(同case1),然后把z的值换成x的值即可。原因很简单,因为x是左子树中的最大值,所以把它作为新的根节点既可以满足x大于所有左子树的值,又可以满足小于所有右子树的值。对于右子树中的最小值也是同理。
这一部分的实现代码如下:(具体实现中把case1和2归为了一类)
node *nextnode(node *x){//找右子树的最小值
node *z=x->right;
while(z->left!=NIL){
z=z->left;
}
return z;
}
void denode(node *z){
node *x,*y;//y表示即将被删除的节点
if(z->left==NIL||z->right==NIL) y=z;//case1或者case2
else y=nextnode(z);//如果是case3,要删除的节点就是右子树的最左节点了
if(y->left!=NIL) x=y->left;//x仅用来标识待删除节点的子树
else x=y->right;
if(x!=NIL) x->parent=y->parent;
if(y->parent==NIL) root=x;
else{
if(y==y->parent->left) y->parent->left=x;
else y->parent->right=x;
}
if(y!=z) z->num=y->num;
delete y;
return ;
}
在实现过程中,需要考虑到根节点的问题。
全部代码实现如下:
#include <cstdio>
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
struct node{
int num;
node *parent,*left,*right;
};
node *root,*NIL;
node *nextnode(node *x){//找右子树的最小值
node *z=x->right;
while(z->left!=NIL){
z=z->left;
}
return z;
}
void denode(node *z){
node *x,*y;//y表示即将被删除的节点
if(z->left==NIL||z->right==NIL) y=z;//case1或者case2
else y=nextnode(z);//如果是case3,要删除的节点就是右子树的最左节点了
if(y->left!=NIL) x=y->left;//x仅用来标识待删除节点的子树
else x=y->right;
if(x!=NIL) x->parent=y->parent;
if(y->parent==NIL) root=x;
else{
if(y==y->parent->left) y->parent->left=x;
else y->parent->right=x;
}
if(y!=z) z->num=y->num;
delete y;
return ;
}
node *find(int a){
node *x=root;
while(x!=NIL){
if(x->num<a) x=x->right;
else if(x->num>a) x=x->left;
else return x;
}
return NIL;
}
void print_inorder(node *x){
if(x==NIL) return ;
print_inorder(x->left);
printf(" %d",x->num);
print_inorder(x->right);
return ;
}
void print_preorder(node *x){
if(x==NIL) return ;
printf(" %d",x->num);
print_preorder(x->left);
print_preorder(x->right);
return ;
}
void insert(int a){
node *x=root,*y=NIL;
node *z=new node();
z->num=a;z->left=NIL;z->right=NIL;
while(x!=NIL){
y=x;
if(z->num < x->num) x=x->left;
else x=x->right;
}
z->parent=y;
if(y==NIL) root=z;
else {
if(z->num < y->num) y->left=z;
else y->right=z;
}
return ;
}
int main(){
int n;
cin>>n;
string s;
node *f;
int a;
for(int i=0;i<n;i++){
cin>>s;
if(s[0]=='i') cin>>a,insert(a);
else if(s[0]=='p'){
print_inorder(root);
printf("\n");
print_preorder(root);
printf("\n");
}
else if(s[0]=='f'){
cin>>a;
f=find(a);
if(f==NIL) printf("no\n");
else printf("yes\n");
}
else{
cin>>a;
denode(find(a));
}
}
return 0;
}
错点:
1.要加上对根节点情况的考虑;
2.AOJ的测试数据比较坑,这题的测试数据,在case3中只能选择右子树中的最左节点,不能选择左子树中的最右节点。
3.对于查找nextnode节点的函数还有疑惑:
// 树的最小值
Node *treeMinimum(Node * x) {
while (x->left != NIL)
x = x->left;
return x;
}
// case 3 ,搜索后一个结点
Node *treeSuccessor(Node *x) {
if (x->right != NIL)
return treeMinimum(x->right);
Node *y = x->parent;
while (y != NIL && x == y->right) {
x = y;
x = y->parent;
}
return y;
}