【数据结构】二叉排序树(c风格、结合c++引用)

目录

1 基本概念

结构体定义

各种接口

2 二叉排序树的构建和中序遍历

递归版单次插入

非递归版单次插入

3 二叉排序树的查找

非递归版本

递归版本

4 二叉排序树的删除(难点)


1 基本概念

        普通二叉排序树是一种简单的数据结构,节点的值根据特定顺序(通常是升序或降序)排列。然而,如果普通二叉排序树不平衡,即左、右子树的高度相差很大时,查询效率可能会降低。因此引出了avl树、红黑树等一系列高阶数据结构。

       基本性质:

  • 若它的左子树不空,则左子树上所有结点的值均小于根结点的值。
  • 若它的右子树不空,则右子树上所有结点的值均大于根结点的值。
  • 它的左、右子树均为为⼆叉排序树。
  • 二叉排序树的查找时间复杂度为树的高度,即为O(以2为底N的对数) ,下面全写成O(logN)
  • 二叉排序树的中序遍历输出是一个递增的数列。

结构体定义

typedef struct BSTreeNode
{int val;struct BSTreeNode* left;struct BSTreeNode* right;
}BSTNode,*BiTree;

各种接口

​​​​​​​

关于用到C++中的引用:

BSTNode是结构体struct BSTNode的别名,BiTree是结构体struct BSTNode指针。

在链表中,首次插入时需要修改头节点,由于头节点的定义也是一个指针,所以要修改一个一级指针,必须传入二级指针或者一级指针的引用,二叉树也是一样,首次插入需要修改根节点的指向,所以这里用引用,当然也可以用二级指针,严蔚敏老师编写的数据结构中也经常用到C++的引用。

而再次或多次进行插入时,我们用cur去遍历链表或二叉树,其实是修改链表和二叉树的一个个结构体,这时我们只需要结构体指针,其实就只需要一级指针即可。

因此,我们直接用二级指针或一级指针的引用,就能解决所有的问题。 


2 二叉排序树的构建和中序遍历

 构建原则:

①根节点为空,先构建根节点。

②插入节点的值小于根节点的值,去根节点的左子树寻找插入位置。

③插入节点的值大于根节点的值,去根节点的右子树寻找插入位置。

void Create(BiTree& root,int* a,int n)
{for (int i = 0; i < n; ++i){BST_InsertR(root, a[i]);//BST_Insert(root, a[i]);}
}

遍历数组O(N),数组每个元素插入O(logN),因此构建的时间复杂度是O(NlogN)

递归版单次插入

int BST_InsertR(BiTree& root, int x)
{//先申请节点BSTNode* newnode = (BiTree)malloc(sizeof(BSTNode));if (newnode == nullptr){perror("malloc fail");exit(-1);}newnode->val = x;newnode->left = newnode->right = nullptr;//进行插入if (root == nullptr)//空树或者走到空{root = newnode;return 1;//插入成功}if (root->val == x)return -1;//插入失败,节点元素值不能相同if (root->val > x)//x小于根节点的值,就去左子树插入return BST_InsertR(root->left, x);if (root->val < x)//x大于于根节点的值,就去右子树插入return BST_InsertR(root->right, x);
}

非递归版单次插入

⭕定义两个指针,cur和prev,prev指向cur的根节点,cur最后走到空,对prev的左右指针进行操作,比对prev->val和x,如果val<x,就让prev->right指向新节点,反之。

int BST_Insert(BiTree& root, int x)
{//二叉排序树左孩子的值比根的值要小,右孩子的值比根的值要大BSTNode* newnode = (BiTree)malloc(sizeof(BSTNode));if (newnode == nullptr){perror("malloc fail");exit(-1);}newnode->val = x;newnode->left = newnode->right = nullptr;//第一次进来root为空if (root == nullptr){root = newnode;return 0;}//第二次开始往后遍历BSTNode* cur = root;BSTNode* prev = nullptr;while (cur)//让cur走到空{prev = cur;if (cur->val < x){cur = cur->right;}else if (cur->val > x){cur = cur->left;}else{return -1;//插入失败,不能有元素相等的情况}}if (prev->val < x){prev->right = newnode;}if (prev->val > x){prev->left = newnode;}return 0;//插入成功
}

假设我们用这个数组去构建一棵树:

结果是这样的:

中序遍历:

void InOrder(BiTree root)
{if (root == nullptr)//空树或走到空return;InOrder(root->left);//左子树printf("%d ", root->val);//根InOrder(root->right);//右子树
}

输出的结果一定是一个递增序列,因此二叉排序树的中序遍历才有意义。

3 二叉排序树的查找

查找原则:

①所查找的值比当前节点的值要小,就去左子树找

②所查找的值比当前节点的值要大,就去右子树找

③查找成功,返回结构体指针BSTNode*/BiTree

二叉排序树的最大查找次数,就是树的深度,类似于折半查找,每查一次排除一半的树。

因此二叉排序树的查找时间复杂度为O(logN) 。

非递归版本

BSTNode* BinarySearch(BiTree root,int x)
{BSTNode* cur = root;while (cur){if (cur->val < x){cur = cur->right;}else if (cur->val > x){cur = cur->left;}else{return cur;}}return nullptr;
}

递归版本

BSTNode* BinarySearchR(BiTree root, int x)
{if (root == nullptr)//空树或者找到空了还没找到return nullptr;if (x == root->val)return root;if (x > root->val)//大于就去右子树找return BinarySearchR(root->right, x);if(x < root->val)//小于就去左子树找return BinarySearchR(root->left, x);
}

4 二叉排序树的删除(难点)

删除原则:

①删除节点的右子树为空,左子树不为空,把左子树顶上来。

②删除节点的左子树为空,右子树不为空,把右子树顶上来。

③删除节点的左右子树都不为空,要么在左子树中找最大的数据和根的数据交换,要么在右子树中找最小的数据和根的数据交换。

void DeleteNode(BiTree& root, int x)
{if (root == nullptr)//找不到或者根为空,直接返回{return;}//先找后删除,递归if (x < root->val){DeleteNode(root->left, x);}if (x > root->val){DeleteNode(root->right, x);}//找到了,执行删除if (root->val == x){if (root->left == nullptr)//左子树为空,把右子树顶上去{BiTree tmp = root;root = root->right;free(tmp);}else if (root->right == nullptr)//右子树为空,把左子树顶上去{BiTree tmp = root;root = root->left;free(tmp);}else//左右子树均不为空,要么在左子树中找最大的数据和根的数据交换,要么在右子树中找最小的数据和根的数据交换//采用前者即可,左子树的最大数据就是左子树的最右结点{BiTree left = root->left;while (left->right){left = left->right;}root->val = left->val;//free(left);//不能这么做,万一这个结点有左子树怎么办?//只能重新在T的左子树找这个结点,复用递归删除这个结点DeleteNode(root->left, left->val);}}
}

图解何为“顶上来” 

由于函数传参用到引用,因此root就是上一层函数root->left或者root->right的别名

定义指针tmp去指向root形参,root形参用root(1)表示一下:

这时我们想让root->right变为root(1)->right,而root(1)就是root->right的别名,因此我们直接让root(1)=root(1)->right,然后去free(tmp),用代码表示就是这样:


同理,右子树为空,把左子树顶上去:


当左右子树都不为空时,要么去左子树中找最大的数据去替换删除节点,要么去右子树中找最小的数据去替换删除节点。

而左子树中的最大数据位于左子树的最右深处节点,右子树中的最小数据位于右子树的最左深处节点。

什么是“替换”:把要删除的根节点的值与左子树最右节点的值交换,然后“删除”掉左子树最右节点;或者把要删除的根节点的值与右子树最左节点的值交换,然后“删除”掉右子树最左节点。

何为删除?真的是直接free掉吗?

 删除59,那它的左子树咋办?直接free就坑了!

复用函数去递归删除59,让59的左子树顶上去:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/167764.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Cortex-M与RISC-V区别

环境 Cortex-M以STM32H750为代表&#xff0c;RISC-V以芯来为代表 RTOS版本为RT-Thread 4.1.1 寄存器 RISC-V 常用汇编 RISC-V 关于STORE x4, 4(sp)这种寄存器前面带数字的写法&#xff0c;其意思为将x4的值存入sp4这个地址&#xff0c;即前面的数字表示偏移的意思 反之LOA…

【LM358AD运放方波振荡器可控输出幅值】2022-2-25

缘由仿真如何缩小方波振荡电路方波幅值?-有问必答-CSDN问答

使用Pytorch从零开始构建LSTM

长短期记忆&#xff08;LSTM&#xff09;网络已被广泛用于解决各种顺序任务。让我们了解这些网络如何工作以及如何实施它们。 就像我们一样&#xff0c;循环神经网络&#xff08;RNN&#xff09;也可能很健忘。这种与短期记忆的斗争导致 RNN 在大多数任务中失去有效性。不过&a…

发送一个网络数据包的过程解析

在 ip_queue_xmit 中&#xff0c;也即 IP 层的发送函数里面&#xff0c;有三部分逻辑。第一部分&#xff0c;选取路由&#xff0c;也即我要发送这个包应该从哪个网卡出去。 这件事情主要由 ip_route_output_ports 函数完成。接下来的调用链为&#xff1a;ip_route_output_port…

改进YOLOv8 | YOLOv5系列:RFAConv续作,即插即用具有任意采样形状和任意数目参数的卷积核AKCOnv

RFAConv续作,构建具有任意采样形状的卷积AKConv 一、论文yolov5加入的方式论文 源代码 一、论文 基于卷积运算的神经网络在深度学习领域取得了显著的成果,但标准卷积运算存在两个固有缺陷:一方面,卷积运算被限制在一个局部窗口,不能从其他位置捕获信息,并且其采样形状是…

Matlab进阶绘图第33期—双曲面图

在《Matlab论文插图绘制模板第56期—曲面图&#xff08;Surf&#xff09;》中&#xff0c;我分享过曲面图的绘制模板。 然而&#xff0c;有的时候&#xff0c;需要在一张图上绘制两个及以上的曲面图&#xff0c;且每个曲面图使用不同的配色方案。 在Matlab中&#xff0c;一张…

C++基础入门(超详细)

话不多说&#xff0c;序言搞起来&#xff1a; 自从开始学老师布置的任务后&#xff0c;目前还是OpenCV&#xff0c;哈~哈。我就莫名问老师&#xff1a;“以后编程是用C还是python&#xff1f;”&#xff0c;果然还是太年轻&#xff0c;老师说&#xff1a;“两们都要精通”。唉&…

set和map + multiset和multimap(使用+封装(RBTree))

set和map 前言一、使用1. set(1)、模板参数列表(2)、常见构造(3)、find和count(4)、insert和erase(5)、iterator(6)、lower_bound和upper_bound 2. multiset3. map(1)、模板参数列表(2)、构造(3)、modifiers和operations(4)、operator[] 4. multimap 二、封装RBTree迭代器原理R…

9.输出国际象棋盘【2023.11.24】

1.问题描述 要求输出国际象棋棋盘。 2.解决思路 国际象棋棋盘由64个黑白相间的格子组成&#xff0c;分为8行*8列。用i控制行&#xff0c;j控制列&#xff0c;根据ij的和的变化来控制输出黑方格还是白方格。 3.代码实现 #include<stdio.h> int main(){for(int i0;i&…

基于广义正态分布算法优化概率神经网络PNN的分类预测 - 附代码

基于广义正态分布算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于广义正态分布算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于广义正态分布优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xf…

网络安全—自学

1.网络安全是什么 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 2.网络安全市场 一、是市场需求量高&#xff1b; 二、则是发展相对成熟…

深度学习之基于Pytorch照片图像转漫画风格网络系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 以下是一个基本的设计介绍&#xff1a; 数据准备&#xff1a;收集足够的真实照片和漫画图像&#xff0c;用于训练模…

typora中的快捷键shift enter 和 enter的交换

1 问题&#xff1a; 我最近在用 typora 进行写作&#xff0c;但是在合格 typora 的 markdown 编辑器很奇怪&#xff0c;它的一个回车符是两次换行&#xff0c;而用 shfit ent 找了半天都不知道怎么解决的这个问题&#xff0c;然后我就去了这个 typora 在 github 开源的问题仓库…

hive 报错return code 40000 from org.apache.hadoop.hive.ql.exec.MoveTask解决思路

参考学习 https://github.com/apache/hive/blob/2b57dd27ad61e552f93817ac69313066af6562d9/ql/src/java/org/apache/hadoop/hive/ql/ErrorMsg.java#L47 为啥学习error code 开发过程中遇到以下错误&#xff0c;大家觉得应该怎么办&#xff1f;从哪方面入手呢&#xff1f; 1.百…

解决在Windows10或Windows11下无权限修改hosts文件

解决在Windows10或Windows11下无权限修改hosts文件&#xff0c;无法写入内容 1、首先在开始菜单中找到这个 2、接着输入&#xff1a; C:\Windows\System32\drivers\etc3、再次输入以下命令行&#xff1a;notepad hosts &#xff0c;并回车&#xff1a; notepad hosts 4、然后…

DataFunSummit:2023年现代数据栈技术峰会-核心PPT资料下载

一、峰会简介 现代数据栈&#xff08;Modern Data Stack&#xff09;是一种集合了多种技术和工具的软件基础设施&#xff0c;旨在更好地管理和处理数据&#xff0c;并为企业提供数据驱动的洞察和决策。包含以下几个组件&#xff1a;数据采集、数据处理、数据存储、数据查询和分…

区块链技术与应用 【全国职业院校技能大赛国赛题目解析】第四套区块链应用后端开发

第四套区块链应用后端开发 环境 : ubuntu20 fisco : 2.8.0 springboot 2.1.1 fisco-java-sdk: 2.7.2 maven 3.8.8 前言 这套后端样题,只涉及调用fisco的系统接口,不涉及此食品溯源项目的业务接口,所以我就直接生成一个springboot项目进行完成此题目。 请提前准备好一…

Docker的项目资源参考

Docker的项目资源包括以下内容&#xff1a; Docker官方网站&#xff1a;https://www.docker.com/ Docker Hub&#xff1a;https://hub.docker.com/ Docker文档&#xff1a;https://docs.docker.com/ Docker GitHub仓库&#xff1a;https://github.com/docker Docker官方博客…

Unity中Shader的Standard材质解析(二)

文章目录 前言一、我们对 Standard 的 PBR 的 GI 进行解析1、我们先创建一个PBR的.cginc文件&#xff0c;用于整理用到的函数2、然后在Standard的Shader中引用该cginc文件 二、依次整理函数到该cginc文件中我们来看一下PBR中GI的镜面反射做了些什么 二、最终代码.cginc代码&…

【教学类-06-07】20231124 (55格版)X-X之间的加法、减法、加减混合题

背景需求 在大四班里&#xff0c;预测试55格“5以内、10以内、20以内的加法题、减法题、加减混合题”的“实用性”。 由于只打印一份20以内加法减法混合题。 “这套20以内的加减法最难”&#xff0c;我询问谁会做&#xff08;摸底幼儿的水平&#xff09; 有两位男孩举手想挑…