目录
二叉搜索树2—搜索和删除
搜索
搜索操作是在二叉搜素树中找出含有指定键值的结点u。
node* find(node* u,int k) {
while (u != nil&&u->key!=k) {
if (u->key > k)u = u->l;
else u = u->r;
}
return u;
}
搜索操作从根结点开始往下搜索键值为k的结点,如果树高为h,复杂度和插入操作一样为O(h)。
删除
删除操作在二叉搜索树中删除结点u。
在删除结点u之后要保持二叉搜索树的性质,所以要分情况讨论,在这之前先学习一下如何找到结点u的后一个结点以及如何找到结点u之后的结点键值的最小值。
搜索u之后结点键值最下值:
node* getminimum(node* u) {
while (u->l != nil)u = u->l;
return u;
}
搜索u后一个结点(中序遍历中排在u后面的那个结点)的代码:
node* getback(node* u) {
if (u->r != nil) {
return getminimum(u->r);
}
else {
node* y = u->p;
while (y != nil && u == y->r) {
u = y;
y = y->p;
}
return y;
}
}
删除结点u分为三种情况:
结点u没有子结点。
结点u有一个子结点。
结点u有两个子结点。
假设u为待删除的结点,实际删除的结点为y,y的子结点为x。那么三种情况为:
u没有子结点时,删除本身,即y=u。
u有一个子结点时,删除本身,即y=u,让u的父结点的子结点变为x,x的父结点变为u的父结点。
u有两个结点时,u后一个结点为y,将y的值复制到u,删除y。(删除y的操作参考步骤一和步骤二)
根据这三点,就得到了删除操作的代码:
void treedelete(node* u) {
if (u->l == nil || u->r == nil) {
y = u;
}
else y = getback(u);
if (y->l != nil) {
x = y->l;
}
else x = y->r;
if (x != nil)x->p = y->p;
if (y->p == nil) {
root = x;
}
else if (y->p->l == y)y->p->l = x;
else y->p->r = x;
if (y != u)u->key = y->key;
delete(y);
}
将插入,搜索,删除结合到一起,实现二叉搜索树最基本的操作:
#include<iostream>
#include<string>
using namespace std;
struct node {
int key;
node* p, * l, * r;
};
node* root, * nil;
node* getminimum(node* u) {
while (u->l!= nil) {
u = u->l;
}
return u;
}
node* getback(node* u) {
if (u->r != nil)return getminimum(u->r);
node* y = u->p;
while (y != nil && u == y->r) {
u = y;
y = y->p;
}
return y;
}
void treedelete(node* z) {
node* y;//要删除的结点
node* x;//y的子结点
if (z->l==nil || z->r == nil) {
y = z;
}else y = getback(z);
if (y->l != nil) {
x = y->l;
}else x = y->r;
if (x != nil)x->p = y->p;
if (y->p == nil)root = x;
else if (y = y->p->l)y->p->l = x;
else y->p->r = x;
if (y != z)z->key = y->key;
delete(y);
}
node* find(node* u,int k) {
while (u != nil && k != u->key) {
if (k >= u->key)u = u->r;
else u = u->l;
}
return u;
}
void insert(int k) {
node* y = nil;
node* x = root;
node* z;
z = new node;
z->key = k;
z->l = nil;
z->r = nil;
while (x != nil) {
y = x;
if (x->key > z->key) {
x = x->l;
}
else x = x->r;
}
z->p = y;
if (y == nil) {
root = z;
}
else if (z->key > y->key) {
y->r = z;
}
else if(z->key <= y->key) y->l = z;
}
void inorder(node* u) {
if (u == nil)return;
inorder(u->l);
printf("%d ", u->key);
inorder(u->r);
}
void preorder(node* u) {
if (u == nil)return;
printf("%d ", u->key);
preorder(u->l);
preorder(u->r);
}
int main() {
int n, i, x;
string com;
scanf("%d", &n);
for (i = 0; i < n; i++) {
cin >> com;
if (com[0] == 'f') {
scanf("%d", &x);
node* t = find(root, x);
if (t != nil)printf("yes\n");
else printf("no\n");
}
if (com[0] == 'i') {
scanf("%d", &x);
insert(x);
}
if (com[0] == 'd') {
scanf("%d", &x);
treedelete(find(root, x));
}
if(com[0]=='p'){
inorder(root);
printf("\n");
preorder(root);
printf("\n");
}
}
return 0;
}
总结
当树高为h时,插入,搜索,删除的算法复杂度都为O(h),也就是说,如果结点数为n,只要输入足够平衡,算法复杂度就是O() 。但是最坏情况下树高会接近n,此时算法复杂度为O(n)。因此要尽量压缩树高,整体分布均匀的二叉搜索树称为平衡二叉搜索树。
读《挑战程序设计竞赛》第二十三天(侵删)2021.3.19
( 2021.7.12 第一次修改)