数据结构——“二叉搜索树”
二叉搜索树是一个很重要的数据结构,它的特殊结构可以在很短的时间复杂度找到我们想要的数据。最坏情况下的时间复杂度是O(n),最好是O(logn)。接下来看一看它的接口函数的实现。
为了使用方便,这里采用模版的方式:
一、节点
template <class K>
struct BSnode
{BSnode(K key):_key(key){}K _key;BSnode* _left = nullptr;BSnode* _right = nullptr;
};
_key用来储存数据,_left和_right用来储存左子树和右子树的节点。
二、搜索树的类的定义
template <class K>
class BSTree
{
private:using Node = BSnode<K>;Node* _root = nullptr;
};
这里typedef了BSnode<K>为Node的类型,方便使用。并创建了根节点,缺省值为空指针。
三、搜索树的插入
搜索树的结构是左子树所有节点的值小于等于根结点的值,右子树所有节点的值大于等于根节点的值。在这里我不考虑等于的情况,即一棵树中不允许出现相同的值。代码如下:
//搜索树的插入bool Insert(K& key){Node* cur = _root;Node* parent = nullptr;if (cur == nullptr){_root = new Node(key);}else{while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else{return false;}}if (key > parent->_key)//插入的值大于父亲节点,那么就需要在父亲节点的右边插入{parent->_right = new Node(key);}else//插入的值小于父亲节点,那么就需要在父亲节点的左边插入{parent->_left = new Node(key);}}return true;}
代码大体情况如下:
1.第一次插入数据的时候,根节点指向空,需要单独讨论。
2.根节点不为空,那么就根据搜索树的特点找到最后插入的位置,申请新节点,连接新节点。
3.找不到插入的位置,即插入的数据已经存在,返回false。
四、搜索树的查找
//搜索树的查找Node* Find(const K& key){assert(_root);Node* cur = _root;while (cur){if (cur->_key > key){cur = cur->_left;}else if (cur->_key < key){cur = cur->_right;}else{return cur;}}return nullptr;}
这段代码是根据搜索树的特点进行查找(搜索的值大于根,那么去右子树查找,小于则去左子树查找),倘若找到,返回该节点指针,找不到,返回空指针。
五、搜索树的删除
搜索树的删除偏向复杂,在我写出的代码中大致分为以下几点:
1.删除的数据在叶子节点上。
2.删除的节点不在叶子节点上,但是它的左右节点至少有一个是空。
3.删除的节点不在叶子节点上,且左右子树都不为空。
4.删除的节点在根节点,根节点至少有一个为空。
通过总结可以精简以上条件:
1.删除的节点的左右节点至少有一个为空。
2.删除的节点的左右节点都不为空。
代码如下:
//搜索二叉树的删除bool Erase(const K& key){assert(_root);//先找到要删除的节点Node* cur = _root;Node* parent = nullptr;while (cur){if (cur->_key > key){parent = cur;cur = cur->_left;}else if (cur->_key < key){parent = cur;cur = cur->_right;}else{break;}}//找不到要删除的数据,返回falseif (cur == nullptr)return false;//找到了//处理“该节点的左孩子或者右孩子为空,或者左右孩子均为空”if (cur->_left == nullptr || cur->_right == nullptr){//该节点是根,且且根节点至少一个子树为空if (parent == nullptr)//parent为空,证明输入的是根节点,且根节点至少一个子树为空 {_root->_left == nullptr ? _root = _root->_right : _root = _root->_left;delete cur;}//该节点是左孩子else if (cur == parent->_left){//左节点为空if (cur->_left == nullptr && cur->_right != nullptr){parent->_left = cur->_right;}//右节点为空else if(cur->_left != nullptr && cur->_right == nullptr){parent->_left = cur->_left;}//左右节点均为空else{parent->_left = nullptr;}//释放资源delete cur;}//该节点是右孩子else if (cur == parent->_right){//左节点为空if (cur->_left == nullptr && cur->_right != nullptr){parent->_right = cur->_right;}//右节点为空else if (cur->_left != nullptr && cur->_right == nullptr){parent->_right = cur->_left;}//左右节点均为空else{parent->_right = nullptr;}//释放资源delete cur;}}
//处理“该节点的左右孩子均不为空”else{//找到左节点的最大值Node* Fparent = cur;Node* Fcur = cur->_left;while (Fcur ->_right){Fparent = Fcur;Fcur = Fcur->_right;}//交换节点的值cur->_key = Fcur->_key;//Fcur的左边有数据if (Fcur->_left){Fparent->_right = Fcur->_left;delete Fcur;}//Fcur的左边没有数据else{if(Fcur == Fparent->_left){Fparent->_left = nullptr;}else{Fparent->_right = nullptr;}delete Fcur;}}return true;}
首先是先要找到删除的数据,若找不到,返回false,若找到,那么进行下一步:
找到后还有如下情况:
1.删除的节点的左右节点至少有一个为空。
对应代码:
//处理“该节点的左孩子或者右孩子为空,或者左右孩子均为空”if (cur->_left == nullptr || cur->_right == nullptr){//该节点是根,且且根节点至少一个子树为空if (parent == nullptr)//parent为空,证明输入的是根节点,且根节点至少一个子树为空 {_root->_left == nullptr ? _root = _root->_right : _root = _root->_left;delete cur;}//该节点是左孩子else if (cur == parent->_left){//左节点为空if (cur->_left == nullptr && cur->_right != nullptr){parent->_left = cur->_right;}//右节点为空else if(cur->_left != nullptr && cur->_right == nullptr){parent->_left = cur->_left;}//左右节点均为空else{parent->_left = nullptr;}//释放资源delete cur;}//该节点是右孩子else if (cur == parent->_right){//左节点为空if (cur->_left == nullptr && cur->_right != nullptr){parent->_right = cur->_right;}//右节点为空else if (cur->_left != nullptr && cur->_right == nullptr){parent->_right = cur->_left;}//左右节点均为空else{parent->_right = nullptr;}//释放资源delete cur;}}
原理:由于删除的节点左右子树至少有一个为空,那么就可以让父节点继承被删除节点的非空节点。继承后,删除该节点。如果被删除的节点的左右子树都为空,即被删除的节点是叶子节点,那么就可以直接删除叶子节点,然后将父节点指向空。
以上代码分别对删除的节点是左子树还是右子树的情况下,删除的节点是否有左子树,是否有右子树,还是左右子树都没有进行讨论,涵盖所有情况。值得注意的是,当搜索树呈现链状的时候,如果删除的是根节点,此时的父节点是空,不能进行访问,那么需要在这里单独讨论。将根节点移动到有数据的那个子节点。
2.删除的节点的左右节点都不为空。
对应代码:
//处理“该节点的左右孩子均不为空”else{//找到左节点的最大值Node* Fparent = cur;Node* Fcur = cur->_left;while (Fcur ->_right){Fparent = Fcur;Fcur = Fcur->_right;}//交换节点的值cur->_key = Fcur->_key;//Fcur的左边有数据if (Fcur->_left){Fparent->_right = Fcur->_left;delete Fcur;}//Fcur的左边没有数据else{if(Fcur == Fparent->_left){Fparent->_left = nullptr;}else{Fparent->_right = nullptr;}delete Fcur;}}
第二种情况就不能直接进行交换。因为父节点没有多余的指针指向被删除节点的左右节点。那么在这里的思想是找到一个比被删除的节点的左孩子大,右孩子小。符合条件的是:左子树的最大值,或者右子树的最小值。找到之后交换节点值。但是还是需要注意的是,找到最大值以后,分为两种情况:
a.最大值的左边为空。
b.最大值的左边不为空。
那么进行讨论:比如删除的数据是8。
a.最大值的左边为空:
这个条件下可以直接交换删除
b.最大值的左边不为空:
这种情况下就需要将父节点的右节点(最大值的位置)指向最大值的左节点。
在这段代码中:
//找到左节点的最大值Node* Fparent = cur;Node* Fcur = cur->_left;while (Fcur ->_right){Fparent = Fcur;Fcur = Fcur->_right;}
Node* Fparent = cur;的目的是避免这种情况下,空指针解引用的问题:删除的数据是3:
倘若Fparent 为空
此时Fcur已经为叶子节点(已经为3的左子树的最大值),while循环不会进而以下代码会对Fparent解引用,造成访问空指针的错误。如果将Fparent复制为cur,那么从一开始,Fparent就是Fcur的父节点,既不违反逻辑,也解决了问题。
因为此时1在父节点的左边,所以综上所述,再删除节点的同时也是需要判断被删除的节点是左节点还是右节点。
示例:
int main()
{BSTree<int> tree;int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };for (auto f : a){tree.Insert(f);}for (auto f : a){tree.Erase(f);tree.InTraversal();cout << endl;}return 0;
}
结果(中序遍历):
相关文章:

数据结构——“二叉搜索树”
二叉搜索树是一个很重要的数据结构,它的特殊结构可以在很短的时间复杂度找到我们想要的数据。最坏情况下的时间复杂度是O(n),最好是O(logn)。接下来看一看它的接口函数的实现。 为了使用方便,这里采用模版的方式: 一、节点 temp…...
Java零基础-Java对象详解
哈喽,各位小伙伴们,你们好呀,我是喵手。运营社区:C站/掘金/腾讯云/阿里云/华为云/51CTO;欢迎大家常来逛逛 今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互…...
从Prompt到创造:解锁AI的无限潜能
文章目录 🍊AI内容创作核心:提示词Prompt1 什么是提示词工程?1.1 提示词的原理是什么?1.2 提示词工程师:百万年薪的职业?1.3 谁都能成为提示词工程师吗? 2 提示词书写的基本技巧3 常见的提示词框架3.1 CO-…...

sqlgun靶场攻略
打开界面 1.输入框测试回显点 -1union select 1,2,3#出现回显点 2.查看数据库名 -1union select 1,2,database()# 3.查看表名 -1union select 1,2,group_concat(table_name) from information_schema.tables where table_schemasqlgunnews# 4.查看admin表中列名 -1union se…...

《网络协议 - HTTP传输协议及状态码解析》
文章目录 一、HTTP协议结构图二、HTTP状态码解读1xx: 信息响应类2xx: 成功响应类3xx: 重定向类4xx: 客户端错误类5xx: 服务器错误类 一、HTTP协议结构图 二、HTTP状态码解读 HTTP状态码(英语:HTTP Status Code)是用以表示网页服务器超文本传…...

9.11 QT ( Day 4)
一、作业 1.Widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTimerEvent> //定时器类 #include <QTime> #include <QtTextToSpeech> //文本转语音类QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEcl…...

利用AI驱动智能BI数据可视化-深度评测Amazon Quicksight(四)
简介 随着生成式人工智能的兴起,传统的 BI 报表功能已经无法满足用户对于自动化和智能化的需求,今天我们将介绍亚马逊云科技平台上的AI驱动数据可视化神器 – Quicksight,利用生成式AI的能力来加速业务决策,从而提高业务生产力。…...

2024.9最新:CUDA安装,pytorch库安装
目录 一、CUDA安装 1.查看自己电脑适配的CUDA的最高版本 2.安装CUDA 3.检查环境变量是否配置,安装是否成功 二、pytorch库安装 1.pytorch库下载 2.选择合适的版本 3.查看版本 一、CUDA安装 1.查看自己电脑适配的CUDA的最高版本 在命令提示符里输入nvidia-…...

Vue3.0组合式API:setup()函数
1、什么是组合式API Vue 3.0 中新增了组合式 API 的功能,它是一组附加的、基于函数的 API,可以更加灵活地组织组件代码。通过组合式 API 可以使用函数而不是声明选项的方式来编写 Vue 组件。因此,使用组合式 API 可以将组件代码编写为多个函…...

利用AI驱动智能BI数据可视化-深度评测Amazon Quicksight(三)
简介 随着生成式人工智能的兴起,传统的 BI 报表功能已经无法满足用户对于自动化和智能化的需求,今天我们将介绍亚马逊云科技平台上的AI驱动数据可视化神器 – Quicksight,利用生成式AI的能力来加速业务决策,从而提高业务生产力。…...

2022高教社杯全国大学生数学建模竞赛C题 问题一(1) Python代码演示
目录 问题 11.1 对这些玻璃文物的表面风化与其玻璃类型、纹饰和颜色的关系进行分析数据探索 -- 单个分类变量的绘图树形图条形图扇形图雷达图Cramer’s V 相关分析统计检验列联表分析卡方检验Fisher检验绘图堆积条形图分组条形图分类模型Logistic回归随机森林import matplotlib…...

Qt QSerialPort数据发送和接收DataComm
文章目录 Qt QSerialPort数据发送和接收DataComm2.添加 Qt Serial Port 模块3.实例源码 Qt QSerialPort数据发送和接收DataComm Qt 框架的Qt Serial Port 模块提供了访问串口的基本功能,包括串口通信参数配置和数据读写,使用 Qt Serial Port 模块就可以…...

macOS上谷歌浏览器的十大隐藏功能
谷歌浏览器(Google Chrome)在macOS上拥有一系列强大而隐蔽的特性,这些功能能显著提高您的浏览体验。从多设备同步到提升安全性和效率,这些被低估的功能等待着被发掘。我们将逐步探索这些功能,帮助您最大化利用谷歌浏览…...

【C++篇】C++类与对象深度解析(二):类的默认成员函数详解
文章目录 【C篇】C类与对象深度解析(二)前言1. 类的默认成员函数2. 构造函数2.1 函数名与类名相同2.2 无返回值2.3 对象实例化时系统会自动调用2.4 构造函数可以重载2.5 默认构造函数的生成规则2.6 无参构造函数与全缺省构造函数的关系2.7 内置类型与自定…...
Linux2-mkdir,touch,cat,more
1.相对路径和绝对路径 cd用于切换目录,对于路径可以用相对路径和绝对路径 例如:cd /home/user/public和cd public效果一样,都是将目录切换到HOME文件夹下的public文件夹 2.特殊路径符 .表示当前目录 ..表示上级目录 ~表示HOME目录 3.m…...

AI 时代程序员的应变之道
一、AI 浪潮来袭,编程界风云变幻 随着 AIGC 大语言模型如 ChatGPT、Midjourney、Claude 等的涌现,AI 辅助编程工具日益普及,程序员的工作方式正经历着深刻的变革。 分析公司 OReilly 日前发布的《2023 Generative AI in the Enterprise》报告…...
SQL编程题复习(24/9/16)
练习题 x40 10-74 获取商品表中商品名称含有“pad”的商品10-75 获取指定商品的商品分类名称(多表查询)10-76 为sh_goods表添加一行记录10-77 检索出sh_goods表中每项keyword对应的商品数量,查询结果显示字段依据输出样例设置10-78 查询sh_go…...
运维工程师面试整理-操作系统
在运维工程师的面试中,操作系统相关的知识通常是重中之重,尤其是Linux/Unix系统。以下是针对操作系统部分的一些详细内容,帮助你更好地准备面试。 1. Linux/Unix 基础 ● 常用命令 ○ 文件和目录管理:ls, cd, cp, mv, rm, mkdir, rmdir, find, grep, awk, sed...

Linux搭建邮箱服务器(简易版)
本章是上一文档的简易版本搭建方式更为快速简洁(只需要两条命令即可搭建),如果想了解更详细一些可以看我上一文档 Linux接发邮件mailx_linux mailx o365-CSDN博客文章浏览阅读857次,点赞25次,收藏19次。本文详细描述了…...

基于SSM的社区爱心捐赠管理系统
作者:计算机学姐 开发技术:SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等,“文末源码”。 专栏推荐:前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于JavaSSMVueMySQL的社区爱…...

【Oracle APEX开发小技巧12】
有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...

(二)TensorRT-LLM | 模型导出(v0.20.0rc3)
0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述,后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作,其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...
Swagger和OpenApi的前世今生
Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章,二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑: 🔄 一、起源与初创期:Swagger的诞生(2010-2014) 核心…...
Rapidio门铃消息FIFO溢出机制
关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系,以下是深入解析: 门铃FIFO溢出的本质 在RapidIO系统中,门铃消息FIFO是硬件控制器内部的缓冲区,用于临时存储接收到的门铃消息(Doorbell Message)。…...

华硕a豆14 Air香氛版,美学与科技的馨香融合
在快节奏的现代生活中,我们渴望一个能激发创想、愉悦感官的工作与生活伙伴,它不仅是冰冷的科技工具,更能触动我们内心深处的细腻情感。正是在这样的期许下,华硕a豆14 Air香氛版翩然而至,它以一种前所未有的方式&#x…...
服务器--宝塔命令
一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行! sudo su - 1. CentOS 系统: yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...
js 设置3秒后执行
如何在JavaScript中延迟3秒执行操作 在JavaScript中,要设置一个操作在指定延迟后(例如3秒)执行,可以使用 setTimeout 函数。setTimeout 是JavaScript的核心计时器方法,它接受两个参数: 要执行的函数&…...

李沐--动手学深度学习--GRU
1.GRU从零开始实现 #9.1.2GRU从零开始实现 import torch from torch import nn from d2l import torch as d2l#首先读取 8.5节中使用的时间机器数据集 batch_size,num_steps 32,35 train_iter,vocab d2l.load_data_time_machine(batch_size,num_steps) #初始化模型参数 def …...
【java】【服务器】线程上下文丢失 是指什么
目录 ■前言 ■正文开始 线程上下文的核心组成部分 为什么会出现上下文丢失? 直观示例说明 为什么上下文如此重要? 解决上下文丢失的关键 总结 ■如果我想在servlet中使用线程,代码应该如何实现 推荐方案:使用 ManagedE…...
算法刷题-回溯
今天给大家分享的还是一道关于dfs回溯的问题,对于这类问题大家还是要多刷和总结,总体难度还是偏大。 对于回溯问题有几个关键点: 1.首先对于这类回溯可以节点可以随机选择的问题,要做mian函数中循环调用dfs(i&#x…...