C++数据结构(搜索二叉树)
1.二叉树搜索的概念
二叉搜索数也成为二叉排序树,它或者是一颗空树,或者是满足以下性质的树:
1.若他的左子树不为空,则左子树上的所有节点的值都小于等于根节点的值。
2.若他的右子树不为空,则右子树上的所有节点的值都大于等于根节点的值。
3.他的左右子树也都是搜索二叉树。
4.二叉搜索树中支持插入相等的值,也可以不支持插入相等的值,具体看使用场景定义。
我们这里实现的是不支持同一值插入的搜索二叉树

1.1搜索二叉树的性能分析
所以在搜索二叉树中,我们如果要找一个值的话,如果需要找的值,比根节点小就向左子树寻找,如果比根节点大我们就去右子树找。这样的话我们查找的次数都是树的高度。
不过我们要注意的是,如果在极端情况下,搜索二叉树会退化成链表的情况,这样的话我们的时间复杂度就成了O(N)的状态了。

左边接近满二叉树时性能可以到O(logN)。右边这种时极端的情况下,这样我们只有O(N)的时间复杂度了。
2.搜索二叉树的实现
2.1定义二叉树的结构
因为二叉树的每个节点不仅仅只储存一个数值,还需要储存孩子节点的地址,所以我们需要定义每个节点的结构:
#pragma once
#include<iostream>
using namespace std;
template <class K>
struct BSTNode
{K _key;BSTNode<K>* _left;BSTNode<K>* _right;BSTNode(const K& key):_key(key),_left(nullptr),_right(nullptr){}
};
这里我们为了应对不同的数据类型我们使用一个模板参数来构建节点,这样我们的节点中的值不仅可以存储整形,还可以存储字符等等。
2.2搜索二叉树的插入(简单)
首先我们这里写搜索二叉树我们直接封装一个类,这里using的作用跟typedef作用相同,在这里用来重命名节点名称,简化代码,后面我们可以直接使用Node即可。
template <class K>
class BSTree
{//typedef BSTNode<K> Node;using Node = BSTNode<K>;
private:Node* _root=nullptr;
};
再插入二叉树节点的时候我们这里首先判断树中有没有节点,如果没有我们这里传进来的值直接充当根节点。
我们这里没有选择使用递归算法,使用简单的循环便可解决问题。
public:bool insert(const K& key){if (_root == nullptr){_root = new Node(key);return true;}Node* parent = nullptr;//这个parent指针用来记录cur节点的父亲节点方便我们进行插入操作Node* cur = _root;//定义cur指针用来找到插入节点的位置while (cur)//循环主题逻辑{if (cur->_key< key){parent = cur;//cur每次寻找都需要更新parent指针cur = cur->_right;}else if (cur->_key > key) {parent = cur;cur = cur->_left;}else {//因为我们实现的是不支持相同值的插入,所以这里相同时直接返回falsereturn false;}}cur = new Node(key);//最后作比较选择插入左边或者右边if (parent->_key < key) { parent->_right = cur; }else { parent->_left = cur; }return true;}
为了验证我们的插入代码的正确性,这里我们先写一个中序遍历进行测试
private:void _Inorder(Node* root){if (root == nullptr)return;_Inorder(root->_left);cout << root->_key << " ";_Inorder(root->_right);}
因为在类中,如果我们需要对树这类结构进行递归调用,我们都是需要根节点,而为了程序的安全性,根节点是存在树类中的private属性中的,所以这个时候我们用一个办法可以完美的解决。就是在类中public中定义一个Inorder函数,在类中去调用_Inorder函数,在外部直接调用Inorder函数,这样就不存在访问不到的问题了。
#pragma once
#include<iostream>
using namespace std;
template <class K>
struct BSTNode
{K _key;BSTNode<K>* _left;BSTNode<K>* _right;BSTNode(const K& key):_key(key),_left(nullptr),_right(nullptr){}
};template <class K>
class BSTree
{//typedef BSTNode<K> Node;using Node = BSTNode<K>;
public:bool insert(const K& key){if (_root == nullptr){_root = new Node(key);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_key< key){parent = cur;cur = cur->_right;}else if (cur->_key > key) {parent = cur;cur = cur->_left;}else {return false;}}cur = new Node(key);if (parent->_key < key) { parent->_right = cur; }else { parent->_left = cur; }return true;}void Inorder(){_Inorder(_root);cout << endl;}private:void _Inorder(Node* root){if (root == nullptr)return;_Inorder(root->_left);cout << root->_key << " ";_Inorder(root->_right);}
private:Node* _root=nullptr;
};
测试函数test.c的代码:
#include"BinarySearch.h"int main()
{int a[] = {14,5,69,45,24,78 };BSTree<int> t;for (auto e : a){t.insert(e);}t.Inorder();return 0;
}
以上就是基本结构和插入操作加上测试代码的完整版(这里是为了让大家看到定义的结构体和类的写法),这里我们来看看运行结果:

根据我们的中序输出的规则是完美契合的,所以我们的插入排序暂时写完。
2.3搜索二叉树的查找(简单)
查找对于插入来说,更加简单,因为插入我们也是一种查找的过程,根据搜索二叉树的规则进行插入。
**实现原理:**这里我们跟插入的思路差不多,定义一个cur指针,一直向下寻找,找到返回当前节点指针,否则返回空。
Node* Find(const K& key)
{Node* cur = _root;while (cur){if (cur->_key < key){cur = cur->_right;}else if(cur->_key>key){cur = cur->_left;}else{return cur;}}return nullptr;
}
在主函数中写个测试函数调用一下:
if (!t.Find(2))
{cout << "nullptr" << endl;
}
elsecout << t.Find(2)->_key << endl;

if (!t.Find(14))
{cout << "nullptr" << endl;
}
elsecout << t.Find(14)->_key << endl;

以上是两个用例,一个是输入的2,我们的树中并没有这个值,所以打印nullptr,输入14,便会打印出来。
2.4搜索二叉树的删除(微难)
搜索二叉树最难的应该就是在这个删除,因为父子节点不仅关系紧密,而且还有他特定的规则,这里我们对于二叉搜索树的删除总结了几个点。
这里我们假设要删掉的节点是(N)
1.要删除的节点N左右孩子均为空,这个时候我们把N节点的父节点对应只想N节点的指针置为空,同时直接N删除节点。
2.要删除的节点N左孩子为空,右孩子不为空,此时把N节点的父亲节点对该孩子的指针指向N的右孩子,同时直接删除N节点。
3.要删除的节点N右孩子为空,左孩子不为空,此时把N节点的父亲节点对该孩子的指针指向N的左孩子,同时直接删除N节点。
4.要删除的孩子左右节点均不为空,此时使用代替删除法,我们找到以N为根节点的左子树中的最大值,或者找以N为根节点的右子树中的最小值来代替N,因为这两个一个是左子树中最大值和右子树最小值,替换后依旧符合搜索二叉树的规则。替换后我们再进行循环对比,如果符合1,2,3这三个情况则可以直接删除。
直接上代码在代码中会有表明注释:
bool Erase(const K& key)
{Node* cur = _root;Node* parent = nullptr;while (cur){//这里跟搜索逻辑相同,找到我们需要删除的节点if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else{//来到这里说明我们已经找到需要删除的节点if (cur->_left == nullptr){//这里我们处理上面的第2条,左孩子为空。//要记得处理的是如果要删除的是根节点,并且没有左孩子,我们直接将根节点的第一个右孩子置为根节点if (cur == _root){_root = _root->_right;}else {//如果不是根节点这个时候我们判断一下要删除的节点是父节点的左孩子还是右孩子,随后将孩子“过继”给父节点if (parent->_left == cur){parent->_left = cur->_right;}else{parent->_left = cur->_right;}}delete cur;}else if (cur->_right == nullptr){//这里是要删除的节点没有右孩子,跟上同理。if (cur == _root){_root = _root->_left;}else {if (parent->_left == cur){parent->_left = cur->_left;}else{parent->_right = cur->_left;}}delete cur;}else{//这里就是最复杂的左右都有孩子的情况Node* parents = cur;//一样的,只要不是连续的空间我们在对节点操作时需要保留上一个节点的位置//这里有两个选择上面我们介绍到了,要不然用左子树的最大节点或者是右子树的最小节点Node* tmp = cur->_left;while (tmp->_right){//这里我们选择找到左边的最大节点parents = tmp;tmp = tmp->_right;}//找到后将tmp节点的值赋给需要删除的节点/cur->_key = tmp->_key;//这里跟上面删除节点同理,直接删掉就好if (parents->_left == tmp){parents->_left = tmp->_left;}else{parents->_right = tmp->_left;}//记得释放空间delete tmp;}return true;}}return false;
}
完全体代码展示(测试代码可以自己去写)
#pragma once
#include<iostream>
using namespace std;
template <class K>
struct BSTNode
{K _key;BSTNode<K>* _left;BSTNode<K>* _right;BSTNode(const K& key):_key(key),_left(nullptr),_right(nullptr){}
};template <class K>
class BSTree
{//typedef BSTNode<K> Node;using Node = BSTNode<K>;
public:bool insert(const K& key){if (_root == nullptr){_root = new Node(key);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_key< key){parent = cur;cur = cur->_right;}else if (cur->_key > key) {parent = cur;cur = cur->_left;}else {return false;}}cur = new Node(key);if (parent->_key < key) { parent->_right = cur; }else { parent->_left = cur; }return true;}Node* Find(const K& key){Node* cur = _root;while (cur){if (cur->_key < key){cur = cur->_right;}else if(cur->_key>key){cur = cur->_left;}else{return cur;}}return nullptr;}bool Erase(const K& key){Node* cur = _root;Node* parent = nullptr;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else{if (cur->_left == nullptr){if (cur == _root){_root = _root->_right;}else {if (parent->_left == cur){parent->_left = cur->_right;}else{parent->_left = cur->_right;}}delete cur;}else if (cur->_right == nullptr){if (cur == _root){_root = _root->_left;}else {if (parent->_left == cur){parent->_left = cur->_left;}else{parent->_right = cur->_left;}}delete cur;}else{Node* parents = cur;Node* tmp = cur->_left;while (tmp->_right){parents = tmp;tmp = tmp->_right;}cur->_key = tmp->_key;if (parents->_left == tmp){parents->_left = tmp->_left;}else{parents->_right = tmp->_left;}delete tmp;}return true;}}return false;}void Inorder(){_Inorder(_root);cout << endl;}private:void _Inorder(Node* root){if (root == nullptr)return;_Inorder(root->_left);cout << root->_key << " ";_Inorder(root->_right);}
private:Node* _root=nullptr;
};
#include"BinarySearch.h"int main()
{int a[] = {14,5,69,45,24,78 };BSTree<int> t;for (auto e : a){t.insert(e);}t.Inorder();t.Erase(5);t.Inorder();return 0;
}
代码测试完全体测试结果:

删除成功!
到此,简单的搜索二叉树已经结束~
相关文章:
C++数据结构(搜索二叉树)
1.二叉树搜索的概念 二叉搜索数也成为二叉排序树,它或者是一颗空树,或者是满足以下性质的树: 1.若他的左子树不为空,则左子树上的所有节点的值都小于等于根节点的值。 2.若他的右子树不为空,则右子树上的所有节点的值…...
OpenCV图像拼接(6)图像拼接模块的用于创建权重图函数createWeightMap()
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 cv::detail::createWeightMap 是 OpenCV 库中用于图像拼接模块的一个函数,主要用于创建权重图。这个权重图在图像拼接过程中扮演着重…...
Micropython RPI-PICO 随记-双PICO串口传数据
开发环境 MCU:双 Pico1(无wifi版),串口相连,需要共地使用固件:自编译版本开发环境:MacBook Pro Sonoma 14.5开发工具:Thonny 4.1.6开发语言:MicroPython 1.24.0 上位机…...
炫酷的HTML5粒子动画特效实现详解
炫酷的HTML5粒子动画特效实现详解 这里写目录标题 炫酷的HTML5粒子动画特效实现详解项目介绍技术栈项目架构1. HTML结构2. 样式设计 核心实现1. 粒子类设计2. 动画效果实现星空效果烟花效果雨滴效果 3. 鼠标交互 性能优化效果展示总结 项目介绍 本文将详细介绍如何使用HTML5 C…...
YoloV8训练和平精英人物检测模型
概述 和平精英人物检测,可以识别游戏中所有人物角色,并通过绘制框将人物选中,训练的模型仅仅具有识别功能,可以识别游戏中的视频、图片等文件,搭配Autox.js可以推理,实现实时绘制,但是对手机性…...
BC93 公务员面试
🚀个人主页:BabyZZの秘密日记 📖收入专栏:C语言练习题分享 🌍文章目入 #include <stdio.h> int main() {int score 0, max 0, min 100, sum 0, count 0; while (scanf("%d", &score) ! EOF){…...
3.0 Disruptor的使用介绍(一)
Disruptor: 其官网定义为:“A High Performance Inter-Thread Messaging Library”,即:线程间的高性能消息框架,与Labview的生产者、消费者模型很相似。 其组成部分比较多,先介绍几个常用的概念: …...
基础实验2-2.1 整数的分类处理
基础实验2-2.1 整数的分类处理 - 浙大版《数据结构学习与实验指导(第2版)》题目集 (pintia.cn) 给定 N 个正整数,要求你从中得到下列三种计算结果: A1 能被 3 整除的最大整数A2 存在整数 K 使之可以表示为 3K1 的整数的个数A3…...
[深度学习]图像分类项目-食物分类
图像分类项目-食物分类(监督学习和半监督学习) 文章目录 图像分类项目-食物分类(监督学习和半监督学习)项目介绍数据处理设定随机种子读取文件内容图像增广定义Dataset类 模型定义迁移学习 定义超参Adam和AdamW 训练过程半监督学习定义Dataset类模型定义定义超参训练过程 项目介…...
有价值的面试问题
迅雷一面 都是c和网络问题 了解epoll吗?解释下水平触发和边缘触发,医院的叫号系统应该算哪一种 c类a有成员b,成员b调用了a的函数,但是a不小心把b的成员删除了,会发生什么,怎么解决 c类a有一个static的函数…...
禁用ONLY_FULL_GROUP_BY模式
这是由于MySQL启用了ONLY_FULL_GROUP_BY模式导致的。以下是禁用该模式的三种方法,结合你的需求选择最合适的方案: 一、临时禁用(重启后失效) 1. 当前会话禁用 直接在SQL客户端执行以下命令,仅对当前数据库连接有效&…...
SAP 获取RFC的WSDL文件
主要是CPI要用到WSDL文件做mapping,客户的SAP服务器不一定直接可在浏览器访问http或者https的地址,所以在SAP里面开发程序内部调用地址获取WSDL文件 *&---------------------------------------------------------------------* *& Report YXX_…...
SQLite优化实践
1. 启用写入批处理 使用事务将多条插入操作包装在一起,这样可以减少磁盘I/O和日志的写入。 BEGIN TRANSACTION; -- 执行多个INSERT语句 COMMIT;通过将多个插入操作包装在一个事务中,可以显著减少每次写入数据库时的磁盘I/O操作。 2. 使用更大的页大小…...
56.fm解调最简单的方法过零检测,如何确定计时器的更新速率
,...
java8循环解压zip文件---实现Excel文件数据追加
java8循环追加Excel数据 实际遇到问题:定期获取zip文件,zip文件内有几个固定模板的Excel文件,有的Excel文件可能还包含多个sheet。 有段时间一次性获取到好几个zip包,需要将这些包都解压,并且按照不同的文件名、sheet进…...
基于SpringBoot的电影售票系统
作者:计算机学姐 开发技术:SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等,“文末源码”。 专栏推荐:前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏:…...
SQL Server 2022 安装问题
一、安装与配置问题 1. SQL Server 2022 安装失败怎么办? 常见原因: 硬件或操作系统不满足最低要求(如内存、磁盘空间不足)。未关闭防火墙或杀毒软件。之前版本的 SQL Server 残留文件未清理。 解决方案: 确保硬件配…...
MySQL 8.0.41安装教程(附安装包)mysql8.0.41图文详细安装教程
文章目录 前言一、MySQL 8.0.41下载安装包二、MySQL 8.0.41安装教程1.启动安装程序2.选择安装模式3.选定安装组件4.确认安装设置5.执行安装操作6.安装进行中7.设置数据库密码8.继续点击下一步9.执行配置操作10.完成配置11. 再次点击下一步12.结束安装向导 三、MySQL 8.0.41配置…...
React Router使用方法
目录 简介React Router的三种使用模式声明模式数据模式框架模式 React Router7声明模式使用方法在入口文件引入BrowserRouter配置一个路由组件管理路由将路由组件引入App.tsx嵌套路由链接式路由导航 \ 和 \<Link>编程式路由导航 简介 React Router 是 React 的多策略路由…...
2025年陕西省各市秦创原产业创新聚集区(机器人、羊乳、苹果)“四链”融合项目申报补贴要求和时间流程
征集2025年陕西省各市秦创原产业创新聚集区(机器人、羊乳、苹果)“四链”融合项目申报补贴要求和时间流程,更多详情请大家参考下文!西安市、宝鸡市、咸阳市、铜川市、渭南市、延安市、榆林市、汉中市、安康市、商洛市10市各地需要…...
深入解析 C++20 中的 std::bind_front:高效函数绑定与参数前置
文章目录 1. 什么是 std::bind_front?2. 使用 std::bind_front2.1 基本用法2.2 绑定多个参数 3. 优势与特点3.1 简化代码3.2 支持可调用对象3.3 支持完美转发 4. 实际应用场景4.1 事件处理4.2 算法通用化4.3 成员函数调用 5. 总结 在现代 C 编程中,函数绑…...
python裁剪nc文件数据
问题描述: 若干个nc文件储存全球的1850-2014年月尺度的mrro数据(或其他数据),从1850-1到2014-12一共1980个月,要提取出最后35年1980.1~2014.12年也就是420个月的数据。 代码实现 def aaa(input_file,output_file,bianliang,start_index,en…...
数据治理之数据仓库
本文主要阐述了数据仓库在大数据平台项目中的地位和重要性,对目前市场上数据仓库主流设计进行分析说明,讲述了通用数据仓库设计上所应考虑的因素。 数据仓库介绍 数据仓库是一个过程而不是一个项目;数据仓库是一个环境,而不是一件产品。数据仓库提供用户用于决策支持的当前…...
QILSTE H6-108QFO高亮橙光LED灯珠 发光二极管LED
# H6-108QFO LED 产品参数解析与应用指南 ## 一、产品概述 H6-108QFO 是一款尺寸为 1.6x0.8x0.55mm 的高亮橙光 LED 产品,采用透明平面胶体设计,符合 EIA 规范标准包装,达到环保 ROHS 要求,防潮等级为 Level 3,适用于…...
2503C++,C++标准的执行
最优雅的应该是c26刚刚引入的std::execution,通过sender/receiver模型和常用的异步算法来简化调用异步逻辑,还可随时改成协程. #include <stdexec/execution.hpp> #include <exec/static_thread_pool.hpp> int main() {exec::static_thread_pool pool(3);auto sch…...
CSS网格布局Grid
目录 一、Grid 网格布局 1.Grid 布局基础 2.网格容器属性 3.网格项目属性 4.高级功能 5.典型应用场景 6.最佳实践 二、Flex和Grid对比 示例: 一、Grid 网格布局 CSS Grid 是一种强大的二维布局系统,能够以行和列的方式精确控制网页布局。它比传…...
微服务架构中的服务发现与 Consul 实践
在微服务架构中,服务之间的通信是核心问题之一。随着服务数量的增长,如何高效地管理和定位服务实例变得尤为重要。本文将介绍服务发现的基本概念,并详细讲解如何使用 Consul 进行服务注册、发现和健康检查。 1. 什么是服务发现? …...
医院挂号预约小程序|基于微信小程序的医院挂号预约系统设计与实现(源码+数据库+文档)
医院挂号预约小程序 目录 基于微信小程序的医院挂号预约系统设计与实现 一、前言 二、系统功能设计 三、系统实现 1、小程序用户端 2、系统服务端 (1) 用户管理 (2)医院管理 (3)医生管理 …...
Emacs 折腾日记(十九)——配置输入法和vim操作方式
上一篇文章中,我们将Emacs变得稍微好看了点。换成了自己喜欢的主题和颜色,这样每天用起来也比较养眼,不会特别排斥。本篇文章的主要任务就是配置输入法方便输入中文以及将vim的操作模式搬到Emacs中。进一步提到Emacs的可用性 配置中文输入法…...
蓝桥杯第十届 特别的数
题目描述 小明对数位中含有 2、0、1、9 的数字很感兴趣(不包括前导 0),在 1 到 40 中这样的数包括 1、2、9、10 至 32、39 和 40,共 28 个,他们的和是 574。 请问,在 1 到 n 中,所有这样的数的…...
