数据结构二叉排序树的 基本操作(C语言)

随机产生一组关键字,利用二叉排序树的插入算法建立二叉排序树,然后删除某一指定关键字元素。

目录

1、二叉排序树的结构

2、插入结点

3、查找结点

4、创建二叉树

5、删除结点

6、完整测试源码


1、二叉排序树的结构

就是一般二叉树的结构。

//二叉排序树
typedef struct BST {
	int data;            //值域
	struct BST* left;    //左孩子
	struct BST* right;    //右孩子
}*BST;

2、插入结点

递归方式,找到要插入值的位置,然后进行插入。

//插入结点
void InsertBSTree(BST* root, int k) {//root是一个二级指针,是二叉树根节点的地址,k是要插入的值
	if (!(*root)) {//当节点为空值时,这就是要插入的位置
		BST p = (BST)malloc(sizeof(struct BST));
		p->data = k;
		p->left = p->right = NULL;
		*root = p;
	}
	else if ((*root)->data >= k)//当前结点值大于要插入的值,那就进入左子树
		InsertBSTree(&(*root)->left, k);
	else						//当前结点值小于要插入的值,那就进入右子树
		InsertBSTree(&(*root)->right, k);
}

3、查找结点

还是用递归,一直比较结点值与要查找的值。

//查找结点
bool FindBSTree(BST root, int k) {
	//找到了返回真,没找到返回假
	if (!root)
		return false;//空树返回假
	if (root->data == k)
		return true;//找到了返回真
	if (root->data >= k)
		return FindBSTree(root->left, k);//去左子树里面找
	if (root->data < k)
		return FindBSTree(root->right, k);//去右子树里面找
}

4、创建二叉树

用随机数函数生成随机数,把生成的随机数当成关键字次次调用插入函数,完成树的创建。

因为rand函数每一次产生的随机数是相同的,所以要用srand函数设置一个随机数种子,保证每一次运行程序生成的随机数不是一样的,所以我们一般用系统时间作随机数种子,因为时间一直在改变;

因为srand取得是系统时间,并且是以秒为单位,但是for循环一次远远小于1秒,这样就会导致种子没有变化,产生的随机数也不会有变化,所以要让两次rand函数之间间隔1秒以上,所以用以恶搞sleep函数。

//创建二叉树
//循环调用插入函数来完成创建
void CreateBSTree(BST* root,int num) {
	int a;//a是随机数作结点值
	for (int i = 0; i < num; i++) {
		srand((unsigned)time(0));//随机数种子
		a = rand() % 100;//0到100范围内的随机数
		Sleep(1000);//停留1s

		if ((*root)&&FindBSTree(*root, a))
        //不为空树并且树里面没有该值,如果树里有这个值就会执行continue语句从而重新生成一个数字
			continue;

		//因为srand((unsigned)time(NULL));取的是系统时间,也就是距离1970.1.1午夜有多少秒。而for循环一次远远小于1s,所以随机值种子不会变化,所以加一个sleep间隔一秒
        //调用插入函数
		InsertBSTree(root, a);
	}
}

5、删除结点

删除结点有四种情况:

  1. 要删除的结点是叶子结点:直接删除该结点
  2. 要删除的结点只有左孩子:左孩子顶替该结点
  3. 要删除的结点只有右孩子:有孩子顶替该节点
  4. 要删除的结点有两个孩子:左子树最右的结点或右子树最左的结点顶替该节点,也就是在中序遍历下找该节点的直接前驱或者直接后继去顶替他。

中序遍历下的二叉排序树是有序的。

//删除结点
//先用函数判断要删除结点的位置
bool DeleteBSTree(BST* root, int k) {
	if ((*root) == NULL)//空树或找不到目标值
		return false;
	if ((*root)->data == k)//找到目标值
		return Delete(root);
	if ((*root)->data > k)//当前结点值大于目标值,进入左子树找
		return DeleteBSTree(&(*root)->left, k);
	if ((*root)->data < k)//当前结点值小于目标值,进入右子树找
		return DeleteBSTree(&(*root)->right, k);
}
//删除的核心操作
bool Delete(BST* root) {
	BST pre, s;//pre作目标节点的前一个,s作要删除的结点
	//1、右子树为空或叶子节点
	if ((*root)->right == NULL) {
		pre = *root;			//保存好要删除的结点
		*root = (*root)->left;	//用他的左儿子代替这个位置
		free(pre);				//再释放掉要删除的结点
	}
	//2、左子树为空
	else if ((*root)->left == NULL) {
		pre = *root;			//保存好要删除的结点
		*root = (*root)->right;	//用他的右儿子代替这个位置
		free(pre);				//再释放掉要删除的结点
	}
	//3、左右子树均不为空
	//用要删除的结点的前驱节点的值来代替要删除的结点的值,然后再把这个前驱节点删除掉就可以了
	else {
		pre = *root;			//保存好要删除的结点
		s = (*root)->left;		//s是他左子树的根节点
		while (s->right) {		//循环找到他左子树的最右的结点,也就是找到要删除结点的前驱节点
			pre = s;			//两个指针依次向下
			s = s->right;		//
		}
		//退出循环,s是被删结点的前驱节点,pre是s的前驱节点
		(*root)->data = s->data;//这里是赋值,用前驱的值代替被删除的结点的值
		if (pre != (*root))		//这里不等于代表着经过了循环,
			pre->right = s->left;//因为前面已经把值赋过去了,后面会有删除,又因为s是(*root)的直接前驱说明s没有右儿子,所以这里把s可能有的左儿子连接到pre上
		else					//这里代表着没有经过循环,也就是说,s直接是(*root)的直接前驱
			pre->left = s->left;
		free(s);				//这里是删除,释放掉这个前驱结点,因为前驱结点的值和儿子都交代完了就可以不要了
	}
	return true;
}

6、完整测试源码

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<windows.h>
#include<stdbool.h>
/*
3.随机产生一组关键字,利用二叉排序树的插入算法建立二叉排序树,然后删除某一指定关键字元素。
*/
//二叉排序树
typedef struct BST {
	int data;
	struct BST* left;
	struct BST* right;
}*BST;
//插入结点
void InsertBSTree(BST* root, int k) {
	if (!(*root)) {//当节点为空值时
		BST p = (BST)malloc(sizeof(struct BST));
		p->data = k;
		p->left = p->right = NULL;
		*root = p;
	}
	else if ((*root)->data >= k)//当前结点值大于要插入的值,那就进入左子树
		InsertBSTree(&(*root)->left, k);
	else						//当前结点值小于要插入的值,那就进入右子树
		InsertBSTree(&(*root)->right, k);
}
//查找结点
bool FindBSTree(BST root, int k) {
	//找到了返回真,没找到返回假
	if (!root)
		return false;//空树返回假
	if (root->data == k)
		return true;//找到了返回真
	if (root->data >= k)
		return FindBSTree(root->left, k);//去左子树里面找
	if (root->data < k)
		return FindBSTree(root->right, k);//去右子树里面找
}
//中序遍历打印二叉树
void Inoder(BST root) {
	if (!root)
		return;
	Inoder(root->left);
	printf("%d ", root->data);
	Inoder(root->right);
}
//创建二叉树
//循环调用插入函数来完成创建,如果
void CreateBSTree(BST* root,int num) {
	int a;//a是随机数作结点值
	for (int i = 0; i < num; i++) {
		srand((unsigned)time(0));//随机数种子
		a = rand() % 100;
		Sleep(1000);//停留1s

		if ((*root)&&FindBSTree(*root, a))//不为空树并且树里面没有该值,如果树里有这个值就会执行continue语句从而重新生成一个数字
			continue;

		//因为srand((unsigned)time(NULL));取的是系统时间,也就是距离1970.1.1午夜有多少秒。而for循环一次远远小于1s,所以随机值种子不会变化,所以加一个sleep间隔一秒
		InsertBSTree(root, a);
	}
}
//删除结点
bool Delete(BST* root) {
	BST pre, s;//pre作目标节点的前一个,s作要删除的结点
	//1、右子树为空或叶子节点
	if ((*root)->right == NULL) {
		pre = *root;			//保存好要删除的结点
		*root = (*root)->left;	//用他的左儿子代替这个位置
		free(pre);				//再释放掉要删除的结点
	}
	//2、左子树为空
	else if ((*root)->left == NULL) {
		pre = *root;			//保存好要删除的结点
		*root = (*root)->right;	//用他的右儿子代替这个位置
		free(pre);				//再释放掉要删除的结点
	}
	//3、左右子树均不为空
	//用要删除的结点的前驱节点的值来代替要删除的结点的值,然后再把这个前驱节点删除掉就可以了
	else {
		pre = *root;			//保存好要删除的结点
		s = (*root)->left;		//s是他左子树的根节点
		while (s->right) {		//循环找到他左子树的最右的结点,也就是找到要删除结点的前驱节点
			pre = s;			//两个指针依次向下
			s = s->right;		//
		}
		//退出循环,s是被删结点的前驱节点,pre是s的前驱节点
		(*root)->data = s->data;//这里是赋值,用前驱的值代替被删除的结点的值
		if (pre != (*root))		//这里不等于代表着经过了循环,
			pre->right = s->left;//因为前面已经把值赋过去了,后面会有删除,又因为s是(*root)的直接前驱说明s没有右儿子,所以这里把s可能有的左儿子连接到pre上
		else					//这里代表着没有经过循环,也就是说,s直接是(*root)的直接前驱
			pre->left = s->left;
		free(s);				//这里是删除,释放掉这个前驱结点,因为前驱结点的值和儿子都交代完了就可以不要了
	}
	return true;
}
bool DeleteBSTree(BST* root, int k) {
	if ((*root) == NULL)//空树或找不到目标值
		return false;
	if ((*root)->data == k)//找到目标值
		return Delete(root);
	if ((*root)->data > k)//当前结点值大于目标值,进入左子树找
		return DeleteBSTree(&(*root)->left, k);
	if ((*root)->data < k)//当前结点值小于目标值,进入右子树找
		return DeleteBSTree(&(*root)->right, k);
}

void menu() {
	printf("---------\n1、插入\n");
	printf("2、删除\n---------\n");
}
void main() {
	BST bstree=NULL;
	int chose;
	int num;//num作插入值
	int num1;//num1作结点个数,
	printf("输入结点个数");
	scanf("%d", &num1);
	printf("请稍等...\n");
	CreateBSTree(&bstree,num1);
	printf("二叉排序树:");
	Inoder(bstree);//先遍历输出一次
	printf("\n");
	while (1) {
		menu();
		scanf("%d", &chose);
		switch (chose) {
		case 1:
			printf("请输入要插入的值:");
			scanf("%d", &num);
			InsertBSTree(&bstree,num);
			printf("新树:");
			Inoder(bstree);
			printf("\n");
			break;
		case 2:
			printf("请输入要删除的值:");
			scanf("%d", &num);
			if (DeleteBSTree(&bstree, num)) {
				printf("新树:");
				Inoder(bstree);
				printf("\n");
			}
			else
				printf("没有这个值!\n");
			break;
		default:return 0;
		}
	}
}

  • 11
    点赞
  • 81
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

多低调

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值