当前位置: 首页 > news >正文

C++数据结构之BST(二叉搜索树)的实现

目录

  • BST 的方法
  • 摘要
  • 查找节点
      • 四个引用,都有妙用
      • 递归版
      • 非递归版
  • 插入节点
      • 利用search的返回值
      • 更新高度的注意事项
      • 插入算法的完整代码
  • 删除节点
      • 框架
      • 单分支,直接替代
      • 双分支,化繁为简
      • 代码
  • code BST

预告:本文是后续实现各种各样平衡二叉搜索树的铺垫。

BST 的方法

方法 功能 参数 返回值
search 查找 T const & val BinNode * &
insert 插入 T const & val BinNode *
remove 移除 T const & val bool

摘要

  1. 虚函数,方便派生类进行重写。
  2. 全局静态模板函数,适用于AVL,Splay,RedBlack等各种BST
  3. 这里的remove一看就是对外的,因为参数终于不是指针了,而是值。需要我们先找位置。

查找节点

四个引用,都有妙用

看到searchIn的声明,居然全都是引用类型。

static BinNode<T> * & searchIn(BinNode<T> * & rt, BinNode<T> * & hot_node, T const & val)

列举这四个引用各自的功能——

返回值引用:插入节点时,这个引用相当于插入位置,后续我们将新节点的指针赋给到这个返回值,父节点的左右孩子之一就会连上新节点。

BinNode<T> * & rt:如果这个不是引用,返回值返回的就是一个仅在函数内部的局部变量(即形参),后续改写这个引用值时,会发生错误。

BinNode<T> * & hot_node:在递归中随深度不断更新这个记忆热点,也是为了方便插入算法,等到最后退出时hot存的是插入位置的父节点。

T const & val:传递引用变量可以提速,为了不误改,前面加上const做约束。

递归版

		virtual BinNode<T> * & search(T const & val){return searchIn(BinTree<T>::root, hot, val);}static BinNode<T> * & searchIn(BinNode<T> * & rt, BinNode<T> * & hot_node, T const & val){if (!rt || rt->data == val) return rt; // 返回的是引用hot_node = rt; //在递归中随深度不断更新if (val < rt->data) return searchIn(rt->left, hot_node, val);else return searchIn(rt->right, hot_node, val);}

非递归版

尾递归转迭代,略。

插入节点

利用search的返回值

有了查找节点算法中“记忆热点”hot的设计,经过search()的运行,就可以得到插入位置的父节点。或许应该记得BinTree里写过的几个函数:insertAsLeft()insertAsRight(),我们只需要将valhot->data做比较即可。在这里,我们换一种写法——不浪费search的返回值。你知道,查找一旦失败,返回值就是NULL的引用,利用它,就无需在insert()中判断究竟应该插入到hot的左边还是右边。

先找到插入位置,X的类型必须是引用,后续我们将新节点的指针赋给到X,hot的左右孩子之一就会连上新节点。

BinNode<T> * & X = search(val); 

下面这一句话将 “父->子” “子->父” 相互关系都连接好了。

X = new BinNode(val, hot); 

更新高度的注意事项

更新高度由于之前做的优化,检测到某处更新后与更新前高度一致则不会再上行更新,所以高度更新要给父节点更新,即updateHighAbove(hot),如果给了X更新,那就不会继续下去。

插入算法的完整代码

		virtual BinNode<T> * insert(T const & val){BinNode<T> * & X = search(val); //为了找到插入位置if (!X){X = new BinNode(val, hot); //这一句话将两个关系连接// 不要忘记BinTree<T>::size++;updateHighAbove(hot);}return X;}

insert()的返回值是X,但返回类型是BinNode<T> *,并不是引用,这在语法中是允许的。所返回的东西仅仅在数值上与X相同,但与X完全脱离了关系。

删除节点

框架

		virtual bool remove(T const & val){BinNode<T> * & X = search(val);if (!X) //树里没有val{return false;}else{removeAt(X, hot);BinTree<T>::size--;updateHighAbove(hot);return true;}}

单分支,直接替代

在这里插入图片描述

双分支,化繁为简

还是想,哪一个节点替代被删节点的位置。那一定是直接后继。求中序遍历下的直接后继。
在这里插入图片描述

代码

		static void removeAt(BinNode<T> * X, BinNode<T> * & hot_node){// hot_node指向要被删除的父亲BinNode<T> * del_node; // 实际要被删除的节点BinNode<T> * succ_node; // 实际要被删除的节点的接替者if (!X->left){del_node = X;succ_node = X->right}else if (!X->right){del_node = X;succ_node = X->left;}else // 双分支情况{ // 找到中序的直接后继del_node = succ(X);succ->node = del_node->right;swap(del_node->data, X->data);BinNode<T>::fromParentTo(del_node) = succ;}hot = del_node->parent;if (succ_node) succ->parent = hot;delete del_node;return succ;}

code BST

# pragma once# include "BinTree.h"template <typename T>
class BST : public BinTree<T> {public:virtual BinNode<T> * & search(T const & val){return searchIn(BinTree<T>::root, hot, val);}virtual BinNode<T> * insert(T const & val){BinNode<T> * & X = search(val); //为了找到插入位置if (!X){X = new BinNode(val, hot); //这一句话将两个关系连接// 不要忘记BinTree<T>::size++;updateHighAbove(hot);}return X;}virtual bool remove(T const & val){BinNode<T> * & X = search(val);if (!X) //树里没有val{return false;}else{removeAt(X, hot);BinTree<T>::size--;updateHighAbove(hot);return true;}}static void removeAt(BinNode<T> * X, BinNode<T> * & hot_node){// hot_node指向要被删除的父亲BinNode<T> * del_node; // 实际要被删除的节点BinNode<T> * succ_node; // 实际要被删除的节点的接替者if (!X->left){del_node = X;succ_node = X->right}else if (!X->right){del_node = X;succ_node = X->left;}else // 双分支情况{ // 找到中序的直接后继del_node = succ(X);succ->node = del_node->right;swap(del_node->data, X->data);BinNode<T>::fromParentTo(del_node) = succ;}hot = del_node->parent;if (succ_node) succ->parent = hot;delete del_node;return succ;}static BinNode<T> * & searchIn(BinNode<T> * & rt, BinNode<T> * & hot_node, T const & val){if (!rt || rt->data == val) return rt; // 返回的是引用hot_node = rt; //在递归中随深度不断更新if (val < rt->data) return searchIn(rt->left, hot_node, val);else return searchIn(rt->right, hot_node, val);}protected:BinNode<T> * hot; // 命中节点的父亲};

相关文章:

C++数据结构之BST(二叉搜索树)的实现

目录 BST 的方法摘要查找节点四个引用&#xff0c;都有妙用递归版非递归版 插入节点利用search的返回值更新高度的注意事项插入算法的完整代码 删除节点框架单分支&#xff0c;直接替代双分支&#xff0c;化繁为简代码 code BST 预告&#xff1a;本文是后续实现各种各样平衡二叉…...

QT以管理员身份运行

以下配置后&#xff0c;QT在QT Creator调试时&#xff0c;或者生成的.exe程序&#xff0c;都将会默认以管理员身份运行。 一、MSVC编译器 1、在Pro文件中添加以下代码&#xff1a; QMAKE_LFLAGS /MANIFESTUAC:\"level\requireAdministrator\ uiAccess\false\\" …...

java中的缓冲流

Java.io.BufferedOutputStream 字节缓冲输出流&#xff0c;继承自OutputStream 构造方法&#xff1a; BufferedOutputStream (OutputStream out) 创建一个新的缓冲输出流&#xff0c;将数据写入指定的底层输出流BufferedOutputStream (OutputStream out, int size) 创建一个新…...

【小吉带你学Git】idea操作(1)_配置环境并进行基本操作

&#x1f38a;专栏【Git】 &#x1f354;喜欢的诗句&#xff1a;更喜岷山千里雪 三军过后尽开颜。 &#x1f386;音乐分享【Counting Stars 】 欢迎并且感谢大家指出小吉的问题&#x1f970; 文章目录 &#x1f354;环境准备⭐配置Git忽略文件&#x1f384;方法&#x1f33a;创…...

DP-GAN-生成器代码

首先看一下数据生成&#xff1a; 在预处理阶段会将label经过ont-hot编码转换为35个通道&#xff0c;即每个通道都是由&#xff08;0,1&#xff09;组成。 在train文件中&#xff0c;对生成器和判别器分别进行更新&#xff0c;根据loss的不同&#xff0c;分别计算对于的损失&a…...

2020-2023中国高等级自动驾驶产业发展趋势研究

1.1 概念界定 2020-2023中国高等级自动驾驶产业发展趋势研究Trends in China High-level Autonomous Driving from 2020 to 2023自动驾驶发展过程中&#xff0c;中国出现了诸多专注于研发L3级以上自动驾驶的公司&#xff0c;其在业界地位也越来越重要。本报告围绕“高等级自动…...

JDK19 - synchronized关键字导致的虚拟线程PINNED

JDK19 - synchronized关键字导致的虚拟线程PINNED 前言一. PINNED是什么意思1.1 synchronized 绑定测试1.2 synchronized 关键字的替代 二. -Djdk.tracePinnedThreads的作用和坑2.1 死锁案例测试2.2 发生原因的推测2.3 总结 前言 在 虚拟线程详解 这篇文章里面&#xff0c;我们…...

用msys2安装verilator并用spinal进行仿真

一 参考 SpinalHDL 开发环境搭建一步到位(图文版) - 极术社区 - 连接开发者与智能计算生态 (aijishu.com)https://aijishu.com/a/1060000000255643Setup and installation of Verilator — SpinalHDL documentation...

【ARM64 常见汇编指令学习 13 -- ARM 汇编 ORG 伪指令学习】

文章目录 ARM ORG 指令介绍UEFI 中对 ORG 指令的使用 ARM ORG 指令介绍 在ARM汇编中&#xff0c;"org"是一个汇编器伪指令&#xff0c;用于设置下一条指令的装入地址。"org"后面跟着的是一个表达式&#xff0c;这个表达式的值就是下一条指令的装入地址。如…...

Vue使用QuillEditor富文本编辑器问题记录

1.内容绑定的问题 绑定内容要使用 v-model:content"xxx" 的形式。 2.设置字体字号 字体以及字号大小的设置需要先注册。 <script> import { QuillEditor,Quill } from vueup/vue-quill import vueup/vue-quill/dist/vue-quill.snow.css; // 设置字体大小 c…...

spring AOP学习

概念 面向切面编程横向扩展动态代理 相关术语 动态代理 spring在运行期&#xff0c;生成动态代理对象&#xff0c;不需要特殊的编译器 Spring AOP的底层就是通过JDK动态代理或者CGLIb动态代理技术为目标Bean执行横向织入 目标对象实现了接口&#xff0c;spring使用JDK的ja…...

16.M端事件和JS插件

16.1移动端 移动端也有自己独特的地方 ●触屏事件touch (也称触摸事件)&#xff0c;Android 和I0S都有。 ●touch对象代表一个触摸点。触摸点可能是一根手指&#xff0c;也可能是一根触摸笔。触屏事件可响应用户手指(或触控笔)对屏幕或者触控板操作。 ●常见的触屏事件如下: …...

Zebec APP:构建全面、广泛的流支付应用体系

目前&#xff0c;流支付协议 Zebec Protocol 基本明确了生态的整体轮廓&#xff0c;它包括由其社区推动的模块化 Layer3 构架的公链 Nautilus Chain、流支付应用 Zebec APP 以及 流支付薪酬工具 Zebec payroll 。其中&#xff0c;Zebec APP 是原有 Zebec Protocol 的主要部分&a…...

Spark 3.1.1 遇到的 from_json regexp_replace组合表达式慢问题的解决

背景 目前公司在从spark 2.4.x升级到3.1.1的时候&#xff0c;遇到了一类SQL极慢的情况&#xff0c;该SQL的如下(只列举了关键的)&#xff1a; select device_personas.* from(selectdevice_id, ads_id, from_json(regexp_replace(device_personas, (?<(\\{|,))"devic…...

Docker 容器常用的命令和操作

1.容器操作 - 运行容器: docker run [OPTIONS] IMAGE [COMMAND] [ARG...] 示例&#xff1a; docker run -it --rm ubuntu /bin/bash - 查看正在运行的容器: docker ps [OPTIONS] 示例&#xff1a; docker ps -a - 停止容器: docker stop CONTAINER [CONTAINER...] 示…...

iTOP-RK3568开发板Windows 安装 RKTool 驱动

在烧写镜像之前首先需要安装 RKTool 驱动。 RKTool 驱动在网盘资料“iTOP-3568 开发板\01_【iTOP-RK3568 开发板】基础资料 \02_iTOP-RK3568 开发板烧写工具及驱动”路径下。 驱动如下图所示&#xff1a; 解压缩后&#xff0c;进入文件夹&#xff0c;如下图所示&#xff1a;…...

nginx rtmp http_flv直播推流

安装配置nginx yum install epel-release -y sudo rpm -Uvh http://li.nux.ro/download/nux/dextop/el7/x86_64/nux-dextop-release-0-5.el7.nux.noarch.rpm yum install ffmpeg ffmpeg-devel -y yum install gcc -y yum install pcre pcre-devel -y yum install openssl open…...

Day50 算法记录| 动态规划 17(子序列)

这里写目录标题 647. 回文子串516.最长回文子序列总结 647. 回文子串 1.动态规划和2.中心扩展 这个视频是基于上面的视频的代码 方法1:动态规划 布尔类型的dp[i][j]&#xff1a;表示区间范围[i,j] &#xff08;注意是左闭右闭&#xff09;的子串是否是回文子串&#xff0c;如…...

RabbitMQ:概念和安装,简单模式,工作,发布确认,交换机,死信队列,延迟队列,发布确认高级,其它知识,集群

1. 消息队列 1.0 课程介绍 1.1.MQ 的相关概念 1.1.1.什么是MQ MQ(message queue&#xff1a;消息队列)&#xff0c;从字面意思上看&#xff0c;本质是个队列&#xff0c;FIFO 先入先出&#xff0c;只不过队列中存放的内容是message 而已&#xff0c;还是一种跨进程的通信机制…...

小研究 - 基于解析树的 Java Web 灰盒模糊测试(二)

由于 Java Web 应用业务场景复杂, 且对输入数据的结构有效性要求较高, 现有的测试方法和工具在测试Java Web 时存在测试用例的有效率较低的问题. 为了解决上述问题, 本文提出了基于解析树的 Java Web 应用灰盒模糊测试方法. 首先为 Java Web 应用程序的输入数据包进行语法建模创…...

Unity Timeline实战:如何用TrackAsset和PlayableBehaviour实现片段跳转循环

Unity Timeline实战&#xff1a;用TrackAsset与PlayableBehaviour构建智能跳转系统 在游戏开发中&#xff0c;过场动画的时间轴控制往往需要更精细的操作。Unity Timeline虽然提供了基础的时间轴编辑功能&#xff0c;但当遇到需要根据游戏状态动态调整播放进度时&#xff0c;原…...

【AI原生研发黄金标准】:20年架构师亲授7步构建高鲁棒性机器学习流水线(附Gartner验证的CI/CD-ML双轨模型)

第一章&#xff1a;AI原生研发范式的本质跃迁 2026奇点智能技术大会(https://ml-summit.org) AI原生研发范式并非对传统软件工程的渐进优化&#xff0c;而是一场以模型为中心、数据为燃料、反馈为闭环的认知重构。它将AI能力从“辅助工具”升维为系统架构的默认构件——开发流…...

一文搞懂 Spring Cloud:从入门到实战的微服务全景指南(建议收藏)妥

一、中间件是啥&#xff1f;咱用“餐厅”打个比方 想象一下&#xff0c;你的FastAPI应用是个高级餐厅。 ?? 顾客&#xff08;客户端请求&#xff09;来到门口。- 迎宾&#xff08;CORS中间件&#xff09;&#xff1a;先看你是不是从允许的街区&#xff08;域名&#xff09;来…...

从安全工具开发视角看驱动遍历:如何用C语言在Windows内核里‘看见’所有sys文件

从安全工具开发视角看驱动遍历&#xff1a;如何用C语言在Windows内核里‘看见’所有sys文件 在安全攻防的战场上&#xff0c;内核层始终是兵家必争之地。当恶意软件试图通过加载隐藏驱动来逃避检测时&#xff0c;安全工程师需要一双能穿透迷雾的"眼睛"——这就是驱动…...

Jenkins 学习总结滩

先唠两句&#xff1a;参数就像餐厅点单 把API想象成一家餐厅的“后厨系统”。 ? 路径参数/dishes/{dish_id} -> 好比你要点“宫保鸡丁”这道具体的菜&#xff0c;它是菜单&#xff08;资源路径&#xff09;的一部分。 查询参数/dishes?spicytrue&typeSichuan -> …...

避坑指南:在Ubuntu 20.04上编译安装GTSAM 4.2并运行因子图示例

深度避坑指南&#xff1a;Ubuntu 20.04下GTSAM 4.2编译安装与因子图实战全解析 当你在Ubuntu 20.04上尝试编译安装GTSAM 4.2时&#xff0c;是否遇到过Python绑定失败、CMake参数配置错误或是依赖版本冲突的困扰&#xff1f;作为机器人感知和SLAM领域的重要工具库&#xff0c;GT…...

为什么92%的LLM项目在Q3前无法通过等保三级?2026奇点大会首次发布《LLM生产安全合规检查清单V2.1》

第一章&#xff1a;2026奇点智能技术大会&#xff1a;LLM生产环境部署指南 2026奇点智能技术大会(https://ml-summit.org) 在真实生产环境中部署大语言模型&#xff0c;需兼顾推理延迟、显存效率、服务可观测性与安全合规性。本次大会实践工作坊基于 Llama-3-70B-Instruct 与 …...

18. UE5 GAS RPG:从数据表格到GE的角色属性动态初始化方案

1. 为什么需要动态属性初始化 在UE5的GAS&#xff08;Gameplay Ability System&#xff09;框架下开发RPG游戏时&#xff0c;角色属性的初始化是个绕不开的话题。刚开始接触GAS时&#xff0c;我也习惯在AttributeSet的构造函数里直接写死初始值&#xff0c;就像这样&#xff1a…...

脑电信号处理避坑指南:用MNE和Matplotlib生成时频图数据集时我踩过的那些雷

脑电信号处理避坑指南&#xff1a;用MNE和Matplotlib生成时频图数据集时我踩过的那些雷 第一次接触EEG-CNN结合的项目时&#xff0c;我天真地以为数据预处理不过是调用几个库函数的简单操作。直到连续三个通宵与各种报错搏斗后&#xff0c;我才明白那些教程里轻描淡写的代码背后…...

XSL-FO 区域

XSL-FO 区域 引言 XSL-FO(可扩展样式表语言格式化对象)是一种用于格式化XML文档的XML方言。它允许开发者定义复杂的布局和格式,以便在多种输出介质上渲染XML数据。XSL-FO的“区域”是其中非常重要的一个概念,它定义了文档中的布局区域,如页边距、页眉、页脚、文本块等。…...