数据结构(8-3)二叉排序树(查找、插入删除)

本文详细介绍了二叉排序树的基础理论,包括其特点和结构。接着,阐述了查找、插入和删除操作的具体步骤,特别讨论了删除操作的三种情况:叶子结点、只有一个孩子的结点以及拥有两个孩子的结点。对于拥有两个孩子的结点,提供了以左子树最大结点或右子树最小结点替换的方法。同时,给出了相应的C语言实现代码,帮助理解每个操作的过程。
摘要由CSDN通过智能技术生成

目录

一、基础理论

1、特点: 

2、结构: 

二、查找

三、插入

四、删除

1、被删除的结点D是叶子结点

2、被删除的结点D仅有一个孩子

2-1、删除结点14(有左无右)

2-2、删除结点 10 (有右无左)

3、被删除结点的左右孩子都存在

方法一:以左子树最大结点替换根结点

方法二:以右子树最小结点替换根结点

总代码

参考资料


一、基础理论

1、特点: 

(1) 若它的左子树不空,则 左子树 上所有结点的值 均小于 它的根结点的值;

(2) 若它的右子树不空,则 右子树 上所有结点的值 均大于 它的根结点的值;

(3) 它的 左、右子树又分别为二叉排序树 。

2、结构: 

二叉树

 

 

二、查找

(1)从根开始,未找到,则获取空结点地址,返回;

(2)相等则找到,提示,退出;

(3)key < 结点,向左遍历(递归lchild

(4)key > 结点,向右遍历(递归rchild

(查找的同时还可以进行定位,最后返回需要查找的位置)

(和二分查找比较相似)

查找结点13

第一步:访问根结点 8 .

第二步:根据二叉排序树的左子树均比根结点小,右子树均比根结点大的性质, 13 > 8 ,因此值为13的结点可能在根结点 8 的右子树当中,我们查看根结点的右子节点 10 :

第三步:与第二步相似, 13 > 10 ,因此查看结点 10 的右孩子 14 :

第四步:根据二叉排序树的左子树均比根结点小,右子树均比根结点大的性质, 13 < 14 ,因此查看 14 的左孩子  13 ,发现刚好和要查找的值相等:

//查找二叉排序树
void Search_BST(pBiTree* T, int key)		//关键字
{
	//查找失败(没有值,需要添加)
	if (!(*T))
	{
		p = T;
		return;
	}
	//查找成功
	if (key == (*T)->data)
	{
		printf("查找成功!\n");
		success = 1;
		return;
	}
	else if (key < (*T)->data)
		Search_BST(&((*T)->lchild), key);		//小,往左遍历
	else
		Search_BST(&((*T)->rchild), key);		//大,往右遍历
}

三、插入

查找定位到待插入的位置,直接进行插入。 

首结点放入根,后面的依次与前面的每一个结点判断,小则放在左边大则放在右边

插入全部

 代码

//插入二叉排序树
void Insert_BST(int key)			//索引
{
	Search_BST(&head, key);
	//找到
	if (success)
		return;
	//未找到(创建新结点,容纳)
	(*p) = (pBiTree)malloc(sizeof(pBiTree));

	(*p)->data = key;				//存储
	//给左右子树赋空值
	(*p)->lchild = NULL;
	(*p)->rchild = NULL;
}

四、删除

删除操作与查找和插入操作不同,我们要分以下三种情况进行处理:

1、被删除的结点D是叶子结点

从二叉排序树当中直接删除即可,也不会影响树的结构。

2、被删除的结点D仅有一个孩子

(1)如果只有左孩子,没有右孩子,那么只需要把要删除结点的左孩子链接到要删除结点的父亲节点,然后删除D结点就好了;(覆盖

(2)如果只有右孩子,没有左孩子,那么只要将要删除结点D的右孩子重接到要删除结点D的父亲结点。(覆盖

2-1、删除结点14(有左无右

覆盖

保存要删除结点 14 的 左孩子 结点 13 到临时变量 temp,并删除结点 14

将删除结点 14 的父结点 10 的 右孩子 设置为 temp,即结点 13

2-2、删除结点 10 (有右无左

覆盖

保存要删除结点 10 的 右孩子 结点 14 到临时变量 temp,并删除结点 10

将删除结点 10 的父结点 8 的 右孩子 设置为 temp,即结点 14

3、被删除结点的左右孩子都存在

用左子树中的最大值或者右子树中的最小值替换顶点

(对于这种情况就复杂一些了,这一次我们将图变得复杂一点儿,给原来结点 10 增加了一个左孩子结点 9 。)

对于上面的二叉排序树的中序遍历结果如下所示:

删除顶点8为例: 

方法一:以左子树最大结点替换根结点

(1)获取左子树中的最大结点7

(2)将删除结点 8 的值替换为 7 

(3)删除根结点左子树当中值最大结点(要考虑好该结点有无左孩子

方法二:以右子树最小结点替换根结点

(1)查找删除结点 8 的右子树当中值最小的结点,即 9 

(2)将删除结点 8 的值替换为 9 

(3)删除根结点右子树当中值最小的结点(要考虑好该结点有无右孩子

代码1:定位到待删除的结点

//删除(定位到删除的结点)
void Delete_BST(pBiTree* p, int key)
{
	if (*p)			//结点存在
	{
		if (key == (*p)->data)
			Delete(p);
		else if (key < (*p)->data)
			Delete_BST(&(*p)->lchild, key);		//递归左
		else
			Delete_BST(&(*p)->rchild, key);		//递归右
	}
}

代码2:单结点删除

//删除结点
void Delete(pBiTree* p)
{
	pBiTree s;
	//1、左右均为空
	if (!(*p)->lchild && !(*p)->rchild)
		*p = NULL;
	//2、左子树为空
	else if (!(*p)->lchild)
		*p = (*p)->rchild;		//覆盖
	//3、右子树为空
	else if (!(*p)->rchild)
		*p = (*p)->lchild;		//覆盖
	//4、左右子树均不为空(两个方法:找左子树最大值/找右子树最小值)
	//这里选择找左子树最大值
	else
	{
		pBiTree q = (*p);
		s = (*p)->lchild;			//先定位左子树
		while (s->rchild)
		{
			q = s;					//保存上一结点
			s = s->rchild;			//往后找
		}
		(*p)->data = s->data;		//覆盖
		//考虑:存在左结点
		p = &q;						//定位到上一节点
		(*p)->rchild = s->lchild;	//重接q右子树
	}
}

总代码

//二叉排序树(查找,插入,删除)
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>

#define MAXSIZE 20
int data[MAXSIZE];
int length = 0;					//数组长度
int success = 0;				//记录是否查找成功

//二叉树结构体
typedef struct BiTree
{
	int data;
	struct BiTree* lchild, * rchild;
}BiTree, * pBiTree, ** p2BiTree;
BiTree* head;
pBiTree* p;

//输入数据
void Input()
{
	int Data;
	int i = 0;
	printf("请输入初始数据:\t");

	//输入数据
	do
	{
		scanf("%d", &Data);
		data[i++] = Data;
	} while (getchar() != '\n');

	length = i;			//记录数组长度
}

//二叉树初始化
void Init_BiTree()
{
	head = NULL;
}

//查找二叉排序树
void Search_BST(pBiTree* T, int key)		//关键字
{
	success = 0;				//初始化,不影响查找结果
	//查找失败(没有值,需要添加)
	if (!(*T))
	{
		p = T;
		return;
	}
	//查找成功
	if (key == (*T)->data)
	{
		printf("查找成功!\n");
		success = 1;
		return;
	}
	else if (key < (*T)->data)
		Search_BST(&((*T)->lchild), key);		//小,往左遍历
	else
		Search_BST(&((*T)->rchild), key);		//大,往右遍历
}

//插入
void Insert_BST(int key)					//索引
{
	Search_BST(&head, key);
	//找到
	if (success)
		return;
	//未找到(创建新结点,容纳)
	(*p) = (pBiTree)malloc(sizeof(pBiTree));

	(*p)->data = key;				//存储
	//给左右子树赋空值
	(*p)->lchild = NULL;
	(*p)->rchild = NULL;
}

//删除结点
void Delete(pBiTree* p)
{
	pBiTree s;
	//1、左右均为空
	if (!(*p)->lchild && !(*p)->rchild)
		*p = NULL;
	//2、左子树为空
	else if (!(*p)->lchild)
		*p = (*p)->rchild;		//覆盖
	//3、右子树为空
	else if (!(*p)->rchild)
		*p = (*p)->lchild;		//覆盖
	//4、左右子树均不为空(两个方法:找左子树最大值/找右子树最小值)
	//这里选择找左子树最大值
	else
	{
		pBiTree q = (*p);
		s = (*p)->lchild;			//先定位左子树
		while (s->rchild)
		{
			q = s;					//保存上一结点
			s = s->rchild;			//往后找
		}
		(*p)->data = s->data;		//覆盖
		//考虑:存在左结点
		p = &q;						//定位到上一节点
		(*p)->rchild = s->lchild;	//重接q右子树
	}
}

//删除(定位到删除的结点)
void Delete_BST(pBiTree* p, int key)
{
	if (*p)			//结点存在
	{
		if (key == (*p)->data)
			Delete(p);
		else if (key < (*p)->data)
			Delete_BST(&(*p)->lchild, key);		//递归左
		else
			Delete_BST(&(*p)->rchild, key);		//递归右
	}
}

//中序遍历输出
void Inorder_Traverse(pBiTree p)
{
	if (p)
	{
		Inorder_Traverse(p->lchild);
		printf("%d ", p->data);
		Inorder_Traverse(p->rchild);
	}
}

int main()
{
	int key;
	Input();							//输入

	Init_BiTree();					    //二叉树初始化

	for (int i = 0; i < length; i++)
		Insert_BST(data[i]);	        //插入二叉排序树

	//删除
	printf("\n请输入需要删除的数据:\t");
	scanf("%d", &key);
	Delete_BST(&head, key);

	//查找
	printf("请输入需要查询的数据:\t");
	scanf("%d", &key);
	Search_BST(&head, key);

	//插入
	printf("\n请输入需要插入的数据:\t");
	scanf("%d", &key);
	Insert_BST(key);
	//查找
	Search_BST(&head, key);

	Inorder_Traverse(head);		//遍历(中序遍历,从小到大输出)

	return 0;
}

参考资料

https://blog.csdn.net/kexuanxiu1163/article/details/106168677?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522162884083916780271545792%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=162884083916780271545792&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-106168677.ecpm_v1_rank_v29&utm_term=%E4%BA%8C%E5%8F%89%E6%8E%92%E5%BA%8F%E6%A0%91&spm=1018.2226.3001.4187

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

_(*^▽^*)_

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

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

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

打赏作者

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

抵扣说明:

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

余额充值