【C++高阶(一)】二叉搜索树深度剖析

💓博主CSDN主页:杭电码农-NEO💓

⏩专栏分类:C++从入门到精通⏪

🚚代码仓库:NEO的学习日记🚚

🌹关注我🫵带你学习C++
  🔝🔝


在这里插入图片描述

这里写目录标题

  • 1. 前言
  • 2. 二叉搜索树的概念以及定义
  • 3. 二叉搜索树的性质
  • 4. 二叉搜索树模拟实现
  • 5. 二叉搜索树的插入操作
  • 6. 二叉搜索树的删除分析(一)
  • 7. 二叉搜索树的删除分析(二)
  • 8. 总结以及拓展

1. 前言

从本篇文章开始正式进入C++高阶
的学习,C++高阶主要包括二叉搜索树
,AVL树,红黑树,哈希等高阶数据结构,
以及C++11和智能指针,抛异常等等.
高阶的内容往往是与普通人拉开差距的
内容,请同学们耐心学习!

本章重点:

本篇文章着重讲解二叉搜索树的概念
以及定义,以及二叉搜索树的模拟实现!
最后拓展讲解二叉搜索树的应用场景.


2. 二叉搜索树的概念以及定义

二叉搜索树又称二叉排序树
它或者是一棵空树

或者是具有以下性质的二叉树:

  • 若它的左子树不为空,则左子树的
    所有节点的值都小于根节点的值

  • 若它的右子树不为空,则右子树的
    所有节点的值都大于根节点的值

  • 它的左右子树也分别为二叉搜索树

比如:

在这里插入图片描述


3. 二叉搜索树的性质

首先,二叉搜索树是有序的!
它的中序遍历出来就是一个有序的序列

上面的图片中,中序遍历出来如下

[1,3,4,6,7,8,10,14,13] 有序序列

其次,二叉搜索树只支持增删查,并不
支持改
,因为随意修改会导致这棵树
可能不满足二叉搜索树的条件,比如
将上图中的14改为9,它就不是二叉
搜索树了,这一点很好理解!

在这里插入图片描述

一点小细节,二叉搜索树中不能出现
值相同的节点,若插入时出现值相同的
节点就直接返回false,插入失败!


4. 二叉搜索树模拟实现

我们先把基本的框架搭建一下:

template<class K>
struct BSTreeNode //二叉搜索树封装的节点信息
{BSTreeNode<K>* _left;BSTreeNode<K>* _right;K _key;BSTreeNode(const K& key):_left(nullptr),_right(nullptr),_key(key){ }
};template<class K>
class BSTree
{typedef BSTreeNode<K> Node;
private:Node* _root = nullptr;
}

对代码的解释:

基本框架比较容易,就是套一层
结构体后使用root,不多解释!


5. 二叉搜索树的插入操作

插入函数是最容易实现的,插入的
值比较大就往右走,比较小就往左走
如果遇见和插入值相同的值,返回false!

bool insert(const K& key)//左小右大
{if (_root == nullptr)//第一次插入时的操作{_root = new Node(key);return true;}Node* cur = _root;Node* prev = nullptr;while (cur != nullptr){if (cur->_key < key){prev = cur;cur = cur->_right;}	else if (cur->_key > key){prev = cur;cur = cur->_left;}else if (cur->_key == key)return false;}cur = new Node(key);if (prev->_key > key)prev->_left = cur;elseprev->_right = cur;return true;
}

对代码的解释:

定义prev的意义是最后cur找到自己的
位置后,要把cur和它的父亲链接起来,
prev起到一个连接作用!最后一步代码
在判断cur是prev的左孩子还是右孩子


6. 二叉搜索树的删除分析(一)

搜索树的删除操作较为复杂
先分析前三种比较简单的情况
我们一步一步来分析:

  1. 被删除的节点无孩子

这种情况是最简单的,直接将此
节点删除了即可,不用做特殊处理!

  1. 被删除的节点只有左孩子

此节点被删除后,将此节点的左孩子
连接到此节点的父亲节点即可!

在这里插入图片描述
3. 被删除的节点只有右孩子

此节点被删除后,将此节点的右孩子
连接到此节点的父亲节点即可!

前三种情况的代码比较容易,直接上菜!

bool erase(const K& key)//非递归版本
{
Node* prev = nullptr;
Node* cur = _root;
while (cur != nullptr)//先找到此节点再删除
{if (cur->_key < key){prev = cur;cur = cur->_right;}else if (cur->_key > key){prev = cur;cur = cur->_left;}else//找到了此节点后,开始删除{//1. 左边为空//2. 右边为空//3. 左右都不为空if (cur->_left == nullptr)//左孩子为空情况{if (cur == _root)_root = cur->_right;else{if (cur == prev->_left)prev->_left = cur->_right;elseprev->_right = cur->_right;}delete cur;return true;}else if (cur->_right == nullptr)//右孩子为空情况{if (cur == _root)_root = cur->_left;else{if (cur == prev->_left)prev->_left = cur->_left;elseprev->_right = cur->_left;}delete cur;return true;}
}

对代码的解释:

看似只写了两种情况,其实把左右都为
空的情况也给算进去了!


7. 二叉搜索树的删除分析(二)

当被删除的节点存在左右孩子时,
此时不再是简单的指向问题了,
这里我使用的是一个常用的方法:

替换法

替换法替换法,和谁替换呢?这个
被替换的节点一定要满足两个条件:

  1. 小于所有右子树的值
  2. 大于所有左子树的值

那么现在就有两个人选了:
一个是左子树中的最右节点
一个是右子树中的最左节点
简单画个图来理解一下:

在这里插入图片描述

假设这里统一使用右子树中最左边的
节点来替换,替换完成后,还有问题!
那就是被替换的节点的左肯定是空了
因为此节点就是最左节点了,但是它的右
不一定为空,还需要分情况讨论,
具体代码如下:

//注,此时的cur即为要删除的节点
Node* tmp = cur->_right;
Node* prevtmp = cur;
while (1) //寻找右子树中的最左节点
{if (tmp->_left != nullptr){prevtmp = tmp;tmp = tmp->_left;}elsebreak;
}
cur->_key = tmp->_key;
if (tmp->_right == nullptr)//如果被替换的节点的右为空
{if (prevtmp == cur)//被删除节点右边只有一个节点,直接将被删除节点的右置空prevtmp->_right = nullptr;elseprevtmp->_left = nullptr;delete tmp;tmp = nullptr;
}
else
{if (prevtmp == cur)prevtmp->_right = tmp->_right;elseprevtmp->_left = tmp->_right;delete tmp;tmp = nullptr;
}

代码解释已放在代码注释中


8. 总结以及拓展

二叉搜索树的模拟实现远远不止于此
但是我们只是想了解它的底层,而不是
写一个更好的二叉树出来,在插入和删除
这两个函数的非递归写法后,还有它们的
递归写法,毕竟一想到树总能想到递归,
递归的插入和删除留给大家自己实现

拓展链接:二叉搜索树的全部代码:

二叉搜索树模拟实现


🔎 下期预告:AVL树深度剖析 🔍

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

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

相关文章

【Spring源码】Spring Event事件

目录 1、前言 2、什么是Spring Event&#xff1f; 3、基本使用 3.1、定义事件 3.2、发布事件 3.3、监听事件 3.3.1、继承ApplicationListener 3.3.2、使用EventListener注解 4、Spring Event是同步还是异步&#xff1f; 4.1、源码实现 4.2、如何实现异步 4.2.1、使用…

【差分放大电路分析】2021-12-31

缘由有哪位愿意帮助一下的-嵌入式-CSDN问答 截图&#xff0c;数值自己去计算。上2图是接电阻&#xff0c;下2图是接三极管。

DNS 区域传输 (AXFR)

漏洞描述 docker环境搭建 使用 AXFR 协议的 DNS 区域传输是跨 DNS 服务器复制 DNS 记录的最简单机制。为了避免在多个 DNS 服务器上编辑信息&#xff0c;可以在一台服务器上编辑信息&#xff0c;并使用 AXFR 将信息复制到其他服务器。但是&#xff0c;如果您不保护您的服务器&…

Javascript每天一道算法题(十八)——矩阵置零-中等

文章目录 1、问题2、示例3、解决方法&#xff08;1&#xff09;方法1——标记数组 1、问题 给定一个 y x x 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 2、示例 示例 1&#xff1a; 输入&#xff1a;matrix [[…

Centos 7、Debian、Ubuntu中tree指令的检查与下载

目录 前言 Centos 7中检查tree指令是否安装的两种办法 which指令检查 查看当前版本指令 不同版本下安装tree指令 Centos 7的发行版本 重点 Debian的发行版本 重点 Ubuntu的发行版本 重点 前言 在大多数Linux发行版中&#xff0c;tree命令通常不是默认安装的指令。…

Python|合并两个字典的8种方法

在Python中&#xff0c;有多种方法可以通过使用各种函数和构造函数来合并字典。在本文中&#xff0c;我们将讨论一些合并字典的方法。 1. 使用方法update() 通过使用Python中的update()方法&#xff0c;可以将一个列表合并到另一个列表中。但是在这种情况下&#xff0c;第二个…

Linux-基本指令(1.0)

Linux是一个非常流行的操作的知识&#xff0c;并提供实例帮助读者更好地理解。让我们一起来学习吧&#xff01;系统&#xff0c;也是云计算、大数据、人工智能等领域的重要基础。学习Linux命令是Linux系统管理的基础&#xff0c;也是开发过程中必不可少的技能。本博客将介绍Lin…

springboot+vue基本微信小程序的旅游社系统

项目介绍 现今市面上有关于旅游信息管理的微信小程序还是比较少的&#xff0c;所以本课题想对如今这么多的旅游景区做一个收集和分类。这样可以给身边喜欢旅游的朋友更好地推荐分享适合去旅行的地方。 前端采用HTML架构&#xff0c;遵循HTMLss JavaScript的开发方式&#xff0…

学C的第十一天【查看汇编代码一步步了解 函数栈帧(栈区局部变量)的创建和销毁】

相关代码gitee自取&#xff1a;C语言学习日记: 加油努力 (gitee.com) 接上期&#xff1a;学C的第十天&#xff08;继续深入学习函数、函数递归、练习&#xff09;-CSDN博客 函数栈帧的创建和销毁 越高级的编译器&#xff0c;越不容易学习和观察该过程 同时在不同的编译器下&…

前缀和——238. 除自身以外数组的乘积

文章目录 &#x1f377;1. 题目&#x1f378;2. 算法原理&#x1f365;解法一&#xff1a;暴力求解&#x1f365;解法二&#xff1a;前缀和&#xff08;积&#xff09; &#x1f379;3. 代码实现 &#x1f377;1. 题目 题目链接&#xff1a;238. 除自身以外数组的乘积 - 力扣&a…

我在electron中集成了自己的ai大模型

同学们可以私信我加入学习群&#xff01; 正文开始 前言一、大模型选择二、获取key三、调用api四、调用ai模型api时&#xff0c;解决跨域总结 前言 最近单位把gpt、文心一言、通义千问、星火等等等等你能想到的ai大模型都给禁掉了&#xff0c;简直丧心病狂。 不知道有多少感同…

leetcode 343.整数拆分 198.打家劫舍(动态规划)

OJ链接 &#xff1a;leetcode 343.整数拆分 代码&#xff1a; class Solution {public int integerBreak(int n) {int[] dp new int[n1];//每个n&#xff0c;拆分多个整数乘积的最大值dp [0] 0;dp [1] 1; for(int i 2 ; i<n; i){for(int j 0 ; j < i; j){dp[i] Ma…

【JavaEE初阶】 网络编程基础与Socket套接字

文章目录 &#x1f38b;网络编程基础&#x1f6a9;为什么需要网络编程&#xff1f;&#x1f6a9;什么是网络编程&#xff1f;&#x1f6a9;网络编程中的基本概念&#x1f4cc;发送端和接收端&#x1f4cc;请求和响应&#x1f4cc;客户端和服务端&#x1f4cc;常见的客户端服务端…

03. Python中的语句

1、前言 在《Python基础数据类型》一文中&#xff0c;我们了解了Python中的基础数据类型&#xff0c;今天我们继续了解下Python中的语句和函数。 2、语句 在Python中常用的语句可以大致分为两类&#xff1a;条件语句、循环语句。 2.1、条件语句 条件语句就是我们编码时常见…

基于Haclon的Blob分析

任务要求&#xff1a; 请用BLOB分析的方法计算图中所有灰度值在120和255之间的像素构成的8连通区域的面积与中心点坐标。 Blob基础&#xff1a; 分析过程&#xff1a;首先获取图像&#xff0c;然后根据特征对原始图像进行阈值分割&#xff08;区分背景像素和前景像素&#xf…

openstack(2)

目录 块存储服务 安装并配置控制节点 安装并配置一个存储节点 验证操作 封装镜像 上传镜像 块存储服务 安装并配置控制节点 创建数据库 [rootcontroller ~]# mysql -u root -pshg12345 MariaDB [(none)]> CREATE DATABASE cinder; MariaDB [(none)]> GRANT ALL PR…

Git工作流和Commit规范

Git大家都非常熟悉了&#xff0c;就不做过多介绍&#xff0c;但是如何用好Git、如何进行合理的分支开发、Merge你是否有一个规范流程呢&#xff1f;&#x1f4a4; 不论是一个团队一起开发一个项目&#xff0c;还是自己独立开发一个项目&#xff0c;都少不了要和Git打交道&…

AI赋能数据表设计

数据表设计软件用过多种&#xff0c;用Ai 设计表几年Ai大模型爆发之后提升了新的高度 用navicat 设计表就是在跟团队的人介绍这次功能的表结构时&#xff0c;没办法看备注&#xff0c;只能看英文字段&#xff0c;导致在比较复杂的表中&#xff0c;总是在表结构和图形结构中来回…

网络和Linux网络_4(应用层)序列化和反序列化(网络计算器)

目录 1. 重新理解协议 2. 网络版本计算器 2.1 前期封装 Log.hpp sock.hpp TcpServer.hpp 第一次测试(链接) 2.2 计算器实现 第二次测试(序列化和反序列化) 第三次测试(客户端字节流) CalServer.cc CalClient.cc 3. 守护进程 3.1 守护进程和前后台进程 3.1 变成…

zlmediakit实现rtsp流服务器

本次实现是将内存中的H264数据经过zlmediakit实现为rtsp流。 我是用的是CAPI的方式&#xff0c;将zlmediakit作为一个sdk嵌入到自己的程序中而不是作为一个独立的进进程服务。 1.编译完成zkmedialit后会得到bin include lib三个文件夹如图 其中bin中的MediaServer是作为独立的…