二叉查找树的基本操作(c语言)

前言

二叉树查找树的基本操作包括查找插入删除,找最大值,找最小值几个操作。

创建一颗二叉树参考之前写的文章:根据树的遍历结果创建树(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

后面的输入自行发挥,有指示的。

效果展示
在这里插入图片描述

  • 27
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值