前言
二叉树查找树的基本操作包括查找,插入,删除,找最大值,找最小值几个操作。
创建一颗二叉树参考之前写的文章:根据树的遍历结果创建树(c语言),关于如何创建一颗二叉树本文会直接使用这篇文章的内容。
预备知识
二叉查找树的特征:
1,若任意节点的左子树不空,则左子树上所有节点的值均小于它的根节点的值。
2,若任意节点的右子树不空,则右子树上所有节点的值均大于它的根节点的值。
3,任意节点的左、右子树也分别为二叉查找树。
4,没有键值相等的节点。
结构体定义
规定每一个根结点存储一个正整数值
struct tree
{
int value;
struct tree* left;
struct tree* right;
};//by wqs
二叉树查找树的查找操作
我们规定,如果找到了就返回树的地址,否则返回NULL代表没有找到。
接下来的这个函数接受一颗已经创建好的树形结构p,以及要查找的数x,函数将返回一个地址。
基本思路就是运用二叉查找树的性质,即左子树小于根节点,根节点小于右子树。
struct tree* find(struct tree* p, int x)
{
if (p == NULL) return NULL;//如果是空树也返回NULL
if (p->value == x) return p;//找到了直接返回该结点的地址
else if (p->value > x) return find(p->left, x);//右边绝对找不到,从左边找
else return find(p->right, x);//左边绝对找不到,从右边找
}//by wqs
二叉查找树的最大值查找和最小值查找操作
根据二叉查找树的性质,二叉查找树的最大值就是最右边的非空树,最小值就是最左边的非空树。
接下来的两个函数接受一颗已经创建好的树p,返回树的最值结点的地址。
在树的删除操作会用到findmin函数。
struct tree* findmax(struct tree* p)
{
if (p == NULL) return NULL;
if (p->right == NULL) return p;
else return findmax(p->right);
}//by wqs
struct tree* findmin(struct tree* p)
{
if (p == NULL) return NULL;
if (p->left == NULL) return p;
else return findmin(p->left);
}//by wqs
二叉查找树的插入操作
特别地,我们规定如果在之前已经有了待插入的元素,那么我们什么都不做。
插入操作首先不能破坏原先创建好的树形结构,就只有从空树去插入,其次插入还必须满足二叉查找树的性质左子树小于根节点,根节点小于右子树于是我们从根节点开始找,如果插入元素小于根节点,则从左边插入,如果插入元素大于根节点,则从右边插入直到遇到空树直接插入。
接下来的这个函数接受一颗已经创建好的树p,以及要插入的数x,返回插入好的树。
struct tree* insert(struct tree* p, int x)
{
if (p == NULL)
{
p = (struct tree*)malloc(sizeof(struct tree));//开辟空间
p->value = x;
p->left = p->right = NULL;//插入完毕
}
if (p->value == x) return p;//什么也不做
else if (p->value > x) p->left = insert(p->left, x);//右子树不变,插入左子树
else p->right = p->right = insert(p->right, x);//左子树不变,插入右子树
return p;
}//by wqs
二叉树查找树的删除操作
二叉查找树的删除不可避免地会打乱原有树的结构,但是即使这样也要保证二叉查找树的基本性质**左子树小于根节点,根节点小于右子树。这样我们分三种情况讨论。
1,如果删除结点的左右子树均为空,则直接删除改结点既可也就是让它等于空,并且释放内存。
如图删除8结点
2,如果删除结点的一个子树为空,一个子树不为空,则让它等于不为空的那颗树,并且释放内存,
删除2结点就让2结点等于1结点(与情况3的删除操作相区别这里是完全让2结点等于1结点),然后释放2结点,由于1结点左右都为空,不要误以为有两个1结点了,原来的1结点位置变成空了。
3,最后一种情况也是最麻烦的情况,待删除结点有两个不为空的子树,这个时候我们要找到右子树的最小值,如果我们要删除6结点那么6结点右子树的最小值就是7我们让6结点的值等于7结点的值,只是让值相等,不改变树形的结构(与情况2完全相等的操作区别)
这样数里面就有了两个值为7的根节点,而且树的结构没有被破坏,我们要删除原来的7结点,这时原来7结点必定对应情况1和情况2,按前面的方法删除即可
这样就成功删除了6结点,其实一共分了两个步骤,首先让6结点变成它右子树的最小值的值,然后再删除它右子树的最小值。
下面是代码实现,我们约定如果为空树,或者树里面没有这个数,我们便什么都不做。
下面的函数接受一个已经创建好的树p,和一个要删除的值x,返回一颗删除好的树。
struct tree* delete(struct tree* p, int x)
{
if (p == NULL) return NULL;//处理删除空树
if (p->value == x)
{
if (p->left != NULL && p->right != NULL)//情况3
{
struct tree* m = findmin(p->right);//找右子树最小值
p->value = m->value;//赋值
p->right = delete(p->right, x);//删除右子树最小值
//else会释放内存,这里不用释放
}
else//对应情况1和情况2,写在一起
{
struct tree* m = p;
if (p->left == NULL) p = p->right;
if (p != NULL && p->right == NULL) p = p->left;//不能用else if否则只能解决情况2
free(m);//这样,情况2变成了不为空的子树,情况1变成了NULL
}
}
else if (p->value > x) p->left = delete(p->left, x);//从左子树删除,右子树必定不变
else p->right = delete(p->right, x);//从右子树删除,左子树必定不变
return p;
}//by wqs
其他代码
创建二叉树的代码,想看详情见前言链接。
struct tree* creattree(int* pre, int* in, int n)
{
if (n <= 0) return NULL;
int* mid = in;
while (*mid != *pre)
{
mid++;
}
struct tree* p = (struct tree*)malloc(sizeof(struct tree));
p->value = *mid;
p->left = creattree(pre + 1, in, mid - in);
p->right = creattree(pre + 1 + (mid - in), mid + 1, n - (mid - in) - 1);
return p;
}//by wqs
先序遍历
void preprint(struct tree* p)
{
if (p == NULL) return;
printf("%d ", p->value);
preprint(p->left);
preprint(p->right);
}//by wqs
中序遍历
void inprint(struct tree* p)
{
if (p == NULL) return;
inprint(p->left);
printf("%d ", p->value);
inprint(p->right);
}//by wqs
后序遍历
void postprint(struct tree* p)
{
if (p == NULL) return;
postprint(p->left);
postprint(p->right);
printf("%d ", p->value);
}//by wqs
打印二叉树三种遍历结果的函数
void structprint(struct tree* p)
{
printf("这棵树的结构是:\n");
printf("先序遍历结果:");
preprint(p);
printf("\n");
printf("中序遍历结果:");
inprint(p);
printf("\n");
printf("后序遍历结果:");
postprint(p);
printf("\n");
}//by wqs
总代码
#include<stdio.h>
#include<stdlib.h>
struct tree
{
int value;
struct tree* left;
struct tree* right;
};
struct tree* creattree(int* pre, int* in, int n);
void preprint(struct tree* p);
void inprint(struct tree* p);
void postprint(struct tree* p);
void structprint(struct tree* p);
struct tree* find(struct tree* p, int x);
struct tree* findmax(struct tree* p);
struct tree* findmin(struct tree* p);
struct tree* insert(struct tree* p, int x);
struct tree* delete(struct tree* p, int x);
int main()
{
int n, i = 0;
printf("一共多少个结点:");
scanf("%d", &n);
int pre[1000] = { 0 }, in[1000] = { 0 };
printf("请保证你的输入是一颗正确的二叉查找树\n");
printf("依次输入先序遍历结果:");
for (i = 0; i < n; i++)
{
scanf("%d", &pre[i]);
}
printf("依次输入中序遍历结果:");
for (i = 0; i < n; i++)
{
scanf("%d", &in[i]);
}
struct tree* p = creattree(pre, in, n);
printf("树形结构创建成功\n");
printf("最大值是%d\n", findmax(p)->value);
printf("最小值是%d\n", findmin(p)->value);
structprint(p);
printf("接下来进入查找操作, 输入1查找,输入0退出\n");
int key, x;
while (1)
{
printf("输入0或1:");
scanf("%d", &key);
if (key == 0) break;
printf("查找:");
scanf("%d", &x);
if (find(p, x) == NULL) printf("没有这个数字\n");
else printf("找到了\n");
}
printf("接下来进入插入操作, 输入1插入,输入0退出,按2打印一次树的结构\n");
while (1)
{
printf("输入0或者1或者2:");
scanf("%d", &key);
if (key == 0) break;
if (key == 2)
{
structprint(p);
continue;
}
printf("插入:");
scanf("%d", &x);
p = insert(p, x);
printf("插入成功\n");
}
printf("接下来进入删除操作, 输入1删除,输入0退出,按2打印一次树的结构\n");
while (1)
{
printf("输入0或者1或者2:");
scanf("%d", &key);
if (key == 0) break;
if (key == 2)
{
structprint(p);
continue;
}
printf("删除:");
scanf("%d", &x);
p = delete(p, x);
printf("删除成功\n");
}
return 0;
}
struct tree* creattree(int* pre, int* in, int n)
{
if (n <= 0) return NULL;
int* mid = in;
while (*mid != *pre)
{
mid++;
}
struct tree* p = (struct tree*)malloc(sizeof(struct tree));
p->value = *mid;
p->left = creattree(pre + 1, in, mid - in);
p->right = creattree(pre + 1 + (mid - in), mid + 1, n - (mid - in) - 1);
return p;
}
void preprint(struct tree* p)
{
if (p == NULL) return;
printf("%d ", p->value);
preprint(p->left);
preprint(p->right);
}
void inprint(struct tree* p)
{
if (p == NULL) return;
inprint(p->left);
printf("%d ", p->value);
inprint(p->right);
}
void postprint(struct tree* p)
{
if (p == NULL) return;
postprint(p->left);
postprint(p->right);
printf("%d ", p->value);
}
void structprint(struct tree* p)
{
printf("这棵树的结构是:\n");
printf("先序遍历结果:");
preprint(p);
printf("\n");
printf("中序遍历结果:");
inprint(p);
printf("\n");
printf("后序遍历结果:");
postprint(p);
printf("\n");
}
struct tree* find(struct tree* p, int x)
{
if (p == NULL) return NULL;
if (p->value == x) return p;
else if (p->value > x) return find(p->left, x);
else return find(p->right, x);
}
struct tree* findmax(struct tree* p)
{
if (p == NULL) return NULL;
if (p->right == NULL) return p;
else return findmax(p->right);
}
struct tree* findmin(struct tree* p)
{
if (p == NULL) return NULL;
if (p->left == NULL) return p;
else return findmin(p->left);
}
struct tree* insert(struct tree* p, int x)
{
if (p == NULL)
{
p = (struct tree*)malloc(sizeof(struct tree));
p->value = x;
p->left = p->right = NULL;
}
if (p->value == x) return p;
else if (p->value > x) p->left = insert(p->left, x);
else p->right = p->right = insert(p->right, x);
return p;
}
struct tree* delete(struct tree* p, int x)
{
if (p == NULL) return NULL;
if (p->value == x)
{
if (p->left != NULL && p->right != NULL)
{
struct tree* m = findmin(p->right);
p->value = m->value;
p->right = delete(p->right, x);
}
else
{
struct tree* m = p;
if (p->left == NULL) p = p->right;
if (p != NULL && p->right == NULL) p = p->left;
free(m);
}
}
else if (p->value > x) p->left = delete(p->left, x);//从左子树删除,右子树必定不变
else p->right = delete(p->right, x);//从右子树删除,左子树必定不变
return p;
}//by wqs
输入样例
这个的输入样例就不给了,只提供给你一棵随机的二叉查找树(也就是前面的输入)。
13
10 3 2 7 6 8 13 11 19 17 16 18 23
2 3 6 7 8 10 11 13 16 17 18 19 23
后面的输入自行发挥,有指示的。
效果展示