二叉排序树详解以及实现

2023-05-16

目录

1.二叉排序树概念

2.二叉排序树的插入

(1)二叉排序树的插入过程

(2)节点插入实现

3.二叉排序树的查找

4.二叉排序树的遍历

5.二叉树排序树节点的删除

(1)删除二叉排序树节点*P

6.完整的流程测试


使用C语言实现二叉树的链式存储

数据结构之折半查找(递归和非递归),插值查找和斐波那契查找

静态树表的查找(最优查找树和次优查找树)

1.二叉排序树概念

二叉排序树(Binary Sort Tree)也称为二叉查找树后者二叉搜索树

  • 具有的性质:
    • 1.若它的左子树不空,则左子树上的所有节点的值均小于它的根节点的值;
    • 2.若它的右子树不空,则右子树上的所有节点的值均大于它的根节点的值;
    • 3.若左右子树都不为空,左右子树也分别为二叉排序树;

二叉排序树结构的定义:

typedef int ElemType;

typedef struct BSTNode{
	ElemType data;
	struct BSTNode*lchild,*rchild;
}*BSTree;

2.二叉排序树的插入

(1)二叉排序树的插入过程

假设:现在有查找的关键字序列为{45,24,53,45,12,24,90},二叉排序树的插入过程如下:

(2)节点插入实现

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<queue>
#include<stack>


void BSTreemenu(){
	printf("---------------1.插入节点--------------\n");
	printf("---------------2.查询节点--------------\n");
	printf("---------------3.删除节点--------------\n");
	printf("---------------4.输出节点--------------\n");
	printf("---------------5.退出程序--------------\n");
}
//构造一棵二叉排序树
int BST_Insert(BSTree&T,int key){
	if(T==NULL){
		T=(BSTree)malloc(sizeof(BSTNode));
		T->data=key;
		T->lchild=NULL;
		T->rchild=NULL;
		return 1;
	}else if(T->data==key){
		return 0;
	}else if(T->data>key){
		BST_Insert(T->lchild,key);
	}else {
		BST_Insert(T->rchild,key);
	}
} 

3.二叉排序树的查找

二叉排序树的查找过程类似于次优二叉树,查找步骤:

  • 1.首先将给定值和根节点的值进行比较,若相等,则查找成功,否则进行第二步;
  • 2.若给定的值大于根节点的值,则到根节点的右子树上进行相同的比较;
  • 3.若给定的值小于根节点的值,则到根节点的左子树上进行相同的比较;
//递归查询节点
BSTNode*BST_Search(BSTree T,int key){
	if(T==NULL)return NULL;
	if(T->data==key){
		return T;
	}else if(T->data>key){
		BST_Search(T->lchild,key);
	}else{
		BST_Search(T->rchild,key);
	}
} 
//非递归查询
BSTNode*BSTSearch(BSTree T,int key){
	while(T!=NULL&&T->data!=key){
		if(T->data>key){
			T=T->lchild; 
		}else{
			T=T->rchild;
		}
	}
} 

4.二叉排序树的遍历

该遍历和二叉树的遍历过程一样,但是这里有一个技巧,就是判断一棵二叉树是否为二叉排序树:如果中序遍历为有序的序列,则为二叉排序树。

//中序遍历二叉排序树(有序)
void  BST_display(BSTree T){
	if(T==NULL)return;
	BST_display(T->lchild);
	printf("%d  ",T->data);
	BST_display(T->rchild);
}

5.二叉树排序树节点的删除

(1)删除二叉排序树节点*P

  • 情况一:
    • 如果*P节点为叶子节点,那么其左右子树都是为NULL,也就是删除此节点并不会破坏整棵树的结构,所以直接删除节点*P即可。

 

 

  • 情况二:
    • 如果*P节点只有左子树PL或者右子树PR,那么只需要将PL或者PR为*P双亲*F的左子树或者右子树即可。

 

  • 情况三:
    • 如果左右子树都不为空的话,那么以上方法则不能使用。由于二叉排序树中序遍历时得到是一个从小到大的序列,在删除节点*P之前,首先找到其*P的直接前驱*S(也就是*P的左子树上最大值的节点)或者直接后继*S(也就是*P右子树上最小值的节点),使用直接前驱或者直接后继的值替代*P节点的值。注意:虽然使用直接前驱或者直接后继的值去替代之后,还是不能直接删除直接前驱或者直接后继,因为对于直接前驱节点来说,有可能其左子树不为空;而对于直接后继节点来说,有可能其右子树不为空,对于上面这种情况的话,使用直接前驱或者直接后继替代*P之后,我们需要将*S的左子树或者右子树挂到*S的双亲节点上去。

 

//删除节点操作
//情况一
void DeleteBSTOne(BSTree T,int key){
	if(!T){
		printf("不存在删除的节点!\n");
	}else{
		if(T->data==key){
			free(T);
			printf("删除成功!\n");
		}else if(T->data>key){
			DeleteBSTOne(T->lchild,key);
		}else{
			DeleteBSTOne(T->rchild,key);
		}
	}
} 
//情况二,三 
void  DeleteBSTwoThree(BSTNode*p){
	//如果*p不存在右子树,那么只需要将左子树挂到双亲上 
	BSTNode*q=NULL;
	if(!p->rchild){
		q=p;
		p=p->lchild;
		free(q);
	}else if(!p->lchild){//否则接右子树 
		q=p;
		p=p->rchild;
		free(q);
	}else{//左右子树都不空情况 
		q=p;
		BSTNode*s=p->lchild;
		//找到直接前驱,也就是*P左子树的最大值 
		while(s->rchild){
			q=s;//q指向节点s的双亲 
			s=s->rchild;
		} 
		p->data=s->data;
		if(q!=p){
			q->rchild=s->lchild;
		}else{
			q->lchild=s->lchild;
		}
		free(s);
	}
	printf("删除节点成功!");
	return ;
}

6.完整的流程测试

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<queue>
#include<stack>

typedef int ElemType;

typedef struct BSTNode{
	ElemType data;
	struct BSTNode*lchild,*rchild;
}*BSTree;

void BSTreemenu(){
	printf("---------------1.插入节点--------------\n");
	printf("---------------2.查询节点--------------\n");
	printf("---------------3.删除节点--------------\n");
	printf("---------------4.输出节点--------------\n");
	printf("---------------5.退出程序--------------\n");
}

//构造一棵二叉排序树
int BST_Insert(BSTree&T,int key){
	if(T==NULL){
		T=(BSTree)malloc(sizeof(BSTNode));
		T->data=key;
		T->lchild=NULL;
		T->rchild=NULL;
		return 1;
	}else if(T->data==key){
		return 0;
	}else if(T->data>key){
		BST_Insert(T->lchild,key);
	}else {
		BST_Insert(T->rchild,key);
	}
} 

//销毁操作
void BST_Destroy(BSTree&T){
	if(T==NULL)return ;
	BST_Destroy(T->lchild);
	BST_Destroy(T->rchild);
	free(T);
} 

//递归查询节点
BSTNode*BST_Search(BSTree T,int key){
	if(T==NULL)return NULL;
	if(T->data==key){
		return T;
	}else if(T->data>key){
		BST_Search(T->lchild,key);
	}else{
		BST_Search(T->rchild,key);
	}
} 
//非递归查询
BSTNode*BSTSearch(BSTree T,int key){
	while(T!=NULL&&T->data!=key){
		if(T->data>key){
			T=T->lchild; 
		}else{
			T=T->rchild;
		}
	}
} 

//中序遍历二叉排序树(有序)
void  BST_display(BSTree T){
	if(T==NULL)return;
	BST_display(T->lchild);
	printf("%d  ",T->data);
	BST_display(T->rchild);
}

//删除节点操作
//情况一
void DeleteBSTOne(BSTree T,int key){
	if(!T){
		printf("不存在删除的节点!\n");
	}else{
		if(T->data==key){
			free(T);
			printf("删除成功!\n");
		}else if(T->data>key){
			DeleteBSTOne(T->lchild,key);
		}else{
			DeleteBSTOne(T->rchild,key);
		}
	}
} 
//情况二,三 
void  DeleteBSTwoThree(BSTNode*p){
	//如果*p不存在右子树,那么只需要将左子树挂到双亲上 
	BSTNode*q=NULL;
	if(!p->rchild){
		q=p;
		p=p->lchild;
		free(q);
	}else if(!p->lchild){//否则接右子树 
		q=p;
		p=p->rchild;
		free(q);
	}else{//左右子树都不空情况 
		q=p;
		BSTNode*s=p->lchild;
		//找到直接前驱,也就是*P左子树的最大值 
		while(s->rchild){
			q=s;//q指向节点s的双亲 
			s=s->rchild;
		} 
		p->data=s->data;
		if(q!=p){
			q->rchild=s->lchild;
		}else{
			q->lchild=s->lchild;
		}
		free(s);
	}
	printf("删除节点成功!");
	return ;
}

int main(){
	BSTree T=NULL;
	int key;
	BSTNode*p; 
	int flag;
	while(1){
		int opt;
		BSTreemenu();
		printf("请输入操作: ");
		scanf("%d",&opt);
		switch(opt){
			case 1:
				printf("请输入元素(end=-1): ");
				scanf("%d",&key);
				while(key!=-1){
					flag=BST_Insert(T,key);
					if(flag==0){
						printf("该元素已经存在\n");
					}
					printf("请输入元素(end=-1): ");
					scanf("%d",&key);
				}
				break;
			case 2:
				printf("请输入要查找的元素: ");
				scanf("%d",&key);
				p=BST_Search(T,key);
				if(p!=NULL){
					printf("查找的元素为: %d\n",p->data);
				}else{
					printf("查找的元素不存在\n");
					//不存在该元素,则插入 
					BST_Insert(T,key);
				}
				break;
			case 3:
				//这里删除节点只操作了叶子节点,关于有左子树(或者右子树),左右子树都不空情况代码已经实现,读者可以
				//自己尝试 
				printf("请输入删除节点的值: ");
				scanf("%d",&key);
				DeleteBSTOne(T,key);
			case 4:
				BST_display(T);
				printf("\n");
				break; 
			default:
				flag=5;
				printf("退出操作:\n");
				BST_Destroy(T);
				break;
		} 
		if(flag==5)break;
	}
	return 0;
}

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

二叉排序树详解以及实现 的相关文章

  • 什么是微服务——微服务架构体系介绍

    Why Microservices 回答这个问题前 xff0c 我们先看下之前大行其道的单体架构 Monolithic Architecture xff0c 对于非专业人士来讲 xff0c 所谓的单体架构 xff0c 其就像一个超大容器 x
  • 微服务架构特征

    一个典型的微服务架构 xff08 MSA xff09 通常包含以下组件 xff1a 客户端 xff1a 微服务架构着眼于识别各种不同的类型的设备 xff0c 以及在此设备上进行的各种管理操作 xff1a 搜索 构建 配置等等身份标识提供者
  • 微服务架构系列——API服务网关

    本章我们简单介绍微服务架构下的API服务网关 xff0c 本章我们将讨论以下话题 xff1a 什么是API服务网关为什么需要API服务网关API服务网关的工作机制 处理横切关注点 当我们在开发设计大型软件应用时 xff0c 我们一般都会采用
  • Java之keytool命令学习

    Java Keytool is a key and certificate management utility It allows users to manage their own public private key pairs an
  • HashMap 与 HashTable的区别

    HashMap 实现了Map接口非线程同步 xff0c 非线程安全不允许重复键键和值均允许为null HashMap lt Interger String gt employeeHashmap 61 new HashMap lt Integ
  • 如何避免敏捷失败?

    很多人都听说敏捷 xff0c 有些人知道敏捷是什么 xff0c 有些人也尝试过敏捷 xff0c 本章中将列举出一些常见的错误敏捷实践 xff0c 如果想要避免敏捷失败 xff0c 建议还是要对照下你所在的敏捷团队中有没有类似的敏捷实践 xf
  • 一个人有文化,到底有多重要?

    关于什么是文化 xff0c 我最最欣赏的回答 xff0c 是作家梁晓声的四句概括 xff1a 根植于内心的修养 xff0c 无需提醒的自觉 xff0c 以约束为前提的自由 xff0c 为别人着想的善良 01 一位叫做 Judy 的空姐 xf
  • MyBatis动态SQL中Map参数处理

    在MyBatis中 xff0c 如果我们需要传递两个参数 xff0c 有一种方式是通过Map作为传入参数 xff0c 在动态SQL中 xff0c 我们需要对传入的Map参数中的值进行判断 xff0c 然后进行动态SQL的条件拼接处理 假设我
  • MyBatis框架下防止SQL注入

    与传统的ORM框架不同 xff0c MyBatis使用XML描述符将对象映射到SQL语句或者存储过程中 xff0c 这种机制可以让我们更大的灵活度通过SQL来操作数据库对象 xff0c 因此 xff0c 我们必须小心这种便利下SQL注入的可
  • 使用android 视频解码mediaCodec碰到的几个问题

    问题1 mediaCodec dequeueInputBuffer一直返回 1 xff0c APP现象 xff1a 视屏卡屏 原因 xff1a 这是因为inputbuffer的内容有误 xff0c 导致无法解码 可通过设延时时间解决 xff
  • 云计算思维导图

    根据近期的云计算学习心得 xff0c 将云计算部分内容制作成思维导图 xff0c 方便于广大云计算学习者作为辅导讲义 xff01 思维导图内容主要包含 xff1a 1 云计算概述 2 云体系结构 3 网络资源 4 存储资源 5 硬件介绍 6
  • 路由器重温——串行链路链路层协议积累

    对于广域网接口来说 xff0c 主要的不同或者说主要的复杂性在于理解不同接口的物理特性以及链路层协议 xff0c 再上层基本都是 IP 协议 xff0c 基本上都是相同的 WAN口中的serial接口主要使用点对点的链路层协议有 xff0c
  • 路由器重温——PPPoE配置管理-2

    四 配置设备作为PPPoE服务器 路由器的PPPoE服务器功能可以配置在物理以太网接口或 PON 接口上 xff0c 也可配置在由 ADSL 接口生成的虚拟以太网接口上 1 配置虚拟模板接口 虚拟模板接口VT和以太网接口或PON接口绑定后
  • Python入门自学进阶——1--装饰器

    理解装饰器 xff0c 先要理解函数和高阶函数 首先要明白 xff0c 函数名就是一个变量 xff0c 如下图 xff0c 定义一个变量名和定义一个函数 xff0c 函数名与变量名是等价的 既然函数名就是一个变量名 xff0c 那么在定义函
  • Python入门自学进阶-Web框架——21、DjangoAdmin项目应用

    客户关系管理 以admin项目为基础 xff0c 扩展自己的项目 一 创建项目 二 配置数据库 xff0c 使用mysql数据库 xff1a 需要安全mysqlclient模块 xff1a pip install mysqlclient D
  • Python入门自学进阶-Web框架——33、瀑布流布局与组合查询

    一 瀑布流 xff0c 是指页面布局中 xff0c 在显示很多图片时 xff0c 图片及文字大小不相同 xff0c 导致页面排版不美观 如上图 xff0c 右边的布局 xff0c 因为第一行第一张图片过长 xff0c 第二行的第一张被挤到第
  • Python入门自学进阶-Web框架——34、富文本编辑器KindEditor、爬虫初步

    KindEditor 是一个轻量级的富文本编辑器 xff0c 应用于浏览器客户端 一 首先是下载 xff1a http kindeditor net down php xff0c 如下图 下载后是 解压缩后 xff1a 红框选中的都可以删除
  • Python入门自学进阶-Web框架——35、网络爬虫使用

    自动从网上抓取信息 xff0c 就是获取相应的网页 xff0c 对网页内容进行抽取整理 xff0c 获取有用的信息 xff0c 保存下来 要实现网上爬取信息 xff0c 关键是模拟浏览器动作 xff0c 实现自动向网址发送请求 xff0c
  • 6、spring的五种类型通知

    spring共提供了五种类型的通知 xff1a 通知类型接口描述Around 环绕通知org aopalliance intercept MethodInterceptor拦截对目标方法调用Before 前置通知org springfram

随机推荐