C++:二叉搜索树的原理和模拟实现
文章目录
- 二叉搜索树
- 二叉搜索树的基本实现原理
- 二叉搜索树的实现
- 非递归版本的实现
- 递归版本的实现
二叉搜索树
二叉搜索树也叫做二叉排序树,可以是空树,也可以是满足一些要求的二叉树
- 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
- 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
- 它的左右子树也分别为二叉搜索树
对于一种数据结构来说,大概率是实现增删查改这四个基本功能,这里实现的是增删查,对于改不实现的原因后续解释:
二叉搜索树的基本实现原理
1. 二叉搜索树的查找
a、从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找
b、最多查找高度次,走到到空,还没找到,这个值不存在
2. 二叉搜索树的插入
插入的具体过程如下:
a. 树为空,则直接新增节点,赋值给root指针
b. 树不空,按二叉搜索树性质查找插入位置,插入新节点
3. 二叉搜索树的删除
首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面四种情况:
a. 要删除的结点无孩子结点
b. 要删除的结点只有左孩子结点
c. 要删除的结点只有右孩子结点
d. 要删除的结点有左、右孩子结点
对于这些情况,有下面的解决方案:
情况b:删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点–直接删除
情况c:删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点–直接删除
情况d:在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点中,再来处理该结点的删除问题–替换法删除
二叉搜索树的实现
二叉树中节点是最基本的信息,因此先进行节点的定义
template <class K>
struct Node
{Node(int key = 0):_left(nullptr), _right(nullptr), _key(key){}Node* _left;Node* _right;K _key;
};
非递归版本的实现
1. 插入
对于二叉搜索树来说,插入的逻辑是很简单的,如果插入的元素比目前的节点要大,就插入到右边,如果比目前的节点小,就插入到左边:
bool Insert(const K& key){if (_root == nullptr){_root = new Node(key);}else{Node* cur = _root;Node* parent = cur;while (cur){parent = cur;if (key > cur->_key){cur = cur->_right;}else if (key < cur->_key){cur = cur->_left;}else{return false;}}if (parent->_key > key){parent->_left = new Node(key);}else{parent->_right = new Node(key);}}return true;}
2. 删除
二叉搜索树的删除较为复杂,下面分几种情况来进行讨论:
- 左根或右根为空

由于这种情况下最多只有一边有值,因此直接删除这个节点即可,令这个节点的父亲节点指向它的下一个节点
- 如果两边都有分支
解决的方法是,从要删除的这个节点的右子树中寻找一个可以替换它位置的数,这个数在寻找的时候选取的是右子树中的最小值,也就是右子树中的最左边的值就是所需要的值,交换后依旧可以满足二叉搜索树的条件,因此可以这样选择
bool Erase(const K& key){Node* cur = _root;Node* parent = cur;while (cur){if (key > cur->_key){parent = cur;cur = cur->_right;}else if (key < cur->_key){parent = cur;cur = cur->_left;}else{if (cur->_left == nullptr){// 左为空if (cur == _root){_root = cur->_right;}else{if (key < parent->_key){parent->_left = cur->_right;}else{parent->_right = cur->_right;}}}else if (cur->_right == nullptr){// 右为空if (cur == _root){_root = cur->_left;}else{if (key < parent->_key){parent->_left = cur->_left;}else{parent->_right = cur->_left;}}}else{// 左右都不为空parent = cur;Node* subleft = cur->_right;while (subleft->_left){parent = subleft;subleft = subleft->_left;}swap(cur->_key, subleft->_key);if (subleft == parent->_left){parent->_left = subleft->_right;}else{parent->_right = subleft->_right;}}return true;}}return false;}
3. 查找
有了前面的基础,查找的原理就很简单了,如果要找的值比当前值小,就到左树中寻找,如果要找的值比当前值大,就到右树中寻找,直到最后找到这个值为止,否则返回找不到
bool Find(const K& key){Node* cur = _root;while (cur){if (key > cur->_key){cur = cur->_right;}else if (key < cur->_key){cur = cur->_left;}else{return true;}}return false;}
递归版本的实现
bool InsertR(const K& key){return _Insert(_root, key);}bool EraseR(const K& key){return _Erase(_root, key);}bool FindR(const K& key){return _Find(_root, key);}void InOrder(){_InOrder(_root);cout << endl;}
private:bool _Insert(Node*& root, const K& key){if (root == nullptr){root = new Node(key);return true;}if (key > root->_key){_Insert(root->_right, key);}else if(key<root->_key){_Insert(root->_left, key);}return false;}bool _Erase(Node*& root, const K& key){if (root==nullptr){return false;}if (key < root->_key){_Erase(root->_left, key);}else if (key > root->_key){_Erase(root->_right, key);}else{if (root->_left == nullptr){Node* del = root;root = root->_right;delete del;return true;}else if (root->_right == nullptr){Node* del = root;root = root->_left;delete del;return true;}else{Node* subleft = root->_right;while (subleft->_left){subleft = subleft->_left;}swap(root->_key, subleft->_key);return _Erase(root->_right, key);}}}bool _Find(Node* root, const K& key){if (root == nullptr){return false;}if (key < root->_key){return _Find(root->_left, key);}else if (key > root->_key){return _Find(root->_right, key);}else{return true;}}void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);}
验证代码是否成功:
int main()
{int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };BSTree<int> bst;cout << "非递归版本:" << endl;for (auto e : a){bst.Insert(e);}bst.InOrder();for (auto e : a){bst.Erase(e);bst.InOrder();}cout << "递归版本:" << endl;for (auto e : a){bst.InsertR(e);}bst.InOrder();for (auto e : a){bst.EraseR(e);bst.InOrder();}return 0;
}
实验结果:

由此可知,这里的二叉搜索树的实现是没有问题的
完整代码:
#include <iostream>
using namespace std;template <class K>
struct Node
{Node(int key = 0):_left(nullptr), _right(nullptr), _key(key){}Node* _left;Node* _right;K _key;
};template <class K>
class BSTree
{typedef Node<K> Node;
public:bool Insert(const K& key){if (_root == nullptr){_root = new Node(key);}else{Node* cur = _root;Node* parent = cur;while (cur){parent = cur;if (key > cur->_key){cur = cur->_right;}else if (key < cur->_key){cur = cur->_left;}else{return false;}}if (parent->_key > key){parent->_left = new Node(key);}else{parent->_right = new Node(key);}}return true;}bool Erase(const K& key){Node* cur = _root;Node* parent = cur;while (cur){if (key > cur->_key){parent = cur;cur = cur->_right;}else if (key < cur->_key){parent = cur;cur = cur->_left;}else{if (cur->_left == nullptr){// 左为空if (cur == _root){_root = cur->_right;}else{if (key < parent->_key){parent->_left = cur->_right;}else{parent->_right = cur->_right;}}}else if (cur->_right == nullptr){// 右为空if (cur == _root){_root = cur->_left;}else{if (key < parent->_key){parent->_left = cur->_left;}else{parent->_right = cur->_left;}}}else{// 左右都不为空parent = cur;Node* subleft = cur->_right;while (subleft->_left){parent = subleft;subleft = subleft->_left;}swap(cur->_key, subleft->_key);if (subleft == parent->_left){parent->_left = subleft->_right;}else{parent->_right = subleft->_right;}}return true;}}return false;}bool Find(const K& key){Node* cur = _root;while (cur){if (key > cur->_key){cur = cur->_right;}else if (key < cur->_key){cur = cur->_left;}else{return true;}}return false;}bool InsertR(const K& key){return _Insert(_root, key);}bool EraseR(const K& key){return _Erase(_root, key);}bool FindR(const K& key){return _Find(_root, key);}void InOrder(){_InOrder(_root);cout << endl;}
private:bool _Insert(Node*& root, const K& key){if (root == nullptr){root = new Node(key);return true;}if (key > root->_key){_Insert(root->_right, key);}else if(key<root->_key){_Insert(root->_left, key);}return false;}bool _Erase(Node*& root, const K& key){if (root==nullptr){return false;}if (key < root->_key){_Erase(root->_left, key);}else if (key > root->_key){_Erase(root->_right, key);}else{if (root->_left == nullptr){Node* del = root;root = root->_right;delete del;return true;}else if (root->_right == nullptr){Node* del = root;root = root->_left;delete del;return true;}else{Node* subleft = root->_right;while (subleft->_left){subleft = subleft->_left;}swap(root->_key, subleft->_key);return _Erase(root->_right, key);}}}bool _Find(Node* root, const K& key){if (root == nullptr){return false;}if (key < root->_key){return _Find(root->_left, key);}else if (key > root->_key){return _Find(root->_right, key);}else{return true;}}void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);}Node* _root = nullptr;
};
相关文章:
C++:二叉搜索树的原理和模拟实现
文章目录 二叉搜索树二叉搜索树的基本实现原理 二叉搜索树的实现非递归版本的实现递归版本的实现 二叉搜索树 二叉搜索树也叫做二叉排序树,可以是空树,也可以是满足一些要求的二叉树 若它的左子树不为空,则左子树上所有节点的值都小于根节点…...
学习视觉CV Transformer (2)--Transformer原理及代码分析
下面结合代码和原理进行深入分析Transformer原理。 2 Transformer深入分析 对于CV初学者来说,其实只需要理解Q K V 的含义和注意力机制的三个计算步骤: Q 和所有 K 计算相似性;对相似性采用 Softmax 转化为概率分布;将概率分布…...
【AI视野·今日CV 计算机视觉论文速览 第271期】Thu, 19 Oct 2023
AI视野今日CS.CV 计算机视觉论文速览 Thu, 19 Oct 2023 Totally 63 papers 👉上期速览✈更多精彩请移步主页 Daily Computer Vision Papers Learning from Rich Semantics and Coarse Locations for Long-tailed Object Detection Authors Lingchen Meng, Xiyang D…...
GoLong的学习之路(四)语法之循环语句
书接上回,上回说到运算符,这次我们说一个编程语言中最重要的一点:流程控制,及循环语句 文章目录 循环语句if else(分支结构)if条件判断特殊写法 for(循环结构)for range(键值循环) switch casegoto(跳转到指定标签)break(跳出循环…...
【Lua语法】字符串
Lua语言中的字符串是不可变值。不能像在C语言中那样直接改变某个字符串中的某个字符,但是可以通过创建一个新字符串的方式来达到修改的目的 print(add2(1 , 2 ,15,3))a "no one"b string.gsub(a , "no" , "on1111")print(a) print…...
程序员节的由来
早在2006年的时候 我就发现了 1024KB1MB 然后恰好又是2的10次方 那时候我就把这一天定义为程序员节了 不过当时并没有太多的知名度。 所以严格意义来讲 距历史记载,程序员应该是由我(田尚滨/cagy)发明的。 As early as 2006 I found …...
订水商城H5实战教程-03用户协议
目录 1 创建页面2 为文本组件增加事件3 检查用户协议是否勾选最终效果 我们上一篇介绍了打开首页时弹出登录窗口的功能,本篇我们实现一下用户协议。 1 创建页面 功能是点击用户协议的时候打开具体的协议内容,需要先创建一个页面。打开自定义应用&#x…...
淘宝app商品详情源数据API接口(解决滑块问题)可高并发采集
通过API接口采集淘宝商品列表和app商品详情遇到滑块验证码的解决方法(带SKU和商品描述,支持高并发),主要是解决了高频情况下的阿里系滑块和必须要N多小号才能解决的反扒问题,以后都可以使用本方法: 大家都…...
xcode15一直显示正在连接iOS17真机问题解决
前言 更新xcode15之后,出现了各种报错问题,可谓是一路打怪啊,解决一个报错问题又来一个。没想到到了最后还能出现一个一直显示正在连接iOS17真机的问题 一直显示正在连接iOS17真机的问题 问题截图如下: 解决方法 1. 打开De…...
stm32通过AT指令与esp8622通信
stm32通过AT指令与esp8622通信 文章目录 stm32通过AT指令与esp8622通信1.tcp通信2.mqtt通信 1.tcp通信 ATCWMODE1 设置为STA模式ATCWJAP_DEF"langtaotech","langtaotechXXX"ATCIPSTA? 查询ipATCIPMUX0 设置单连接ATCIPSTART"TCP","19…...
Flutter 类似onResume 监听,解决入场动画卡顿
在Flutter 实际开发过程中,页面数据往往是异步加载,接口请求回来后,数据刷新显示到界面上。 由于Flutter性能原因,也可能因为获取数据量比较大,在新页面路由进场动画执行过程中,接口请求结果回来了&#x…...
1024勋章
🌸关于重阳节的一些发疯日常(昨天的聊天记录,今天发系列)😅 🌸没错,发出来单纯觉得好玩儿😉(为了1024勋章😏)芜湖!...
C++栈、队列、优先级队列模拟+仿函数
目录 一、栈的模拟和deque容器 1.deque 1.1deque结构 1.2deque优缺点 2.stack模拟 二、队列的模拟 三、priority_queue优先级队列 1.优先级队列模拟 2.添加仿函数 一、栈的模拟和deque容器 在之前,我们学过了C语言版本的栈,可以看这篇文章 栈和…...
ES挂载不上怎么处理?
全文搜索 EelasticSearch安装 Docker安装 docker run -d --name es7 -e ES_JAVA_POTS"-Xms256m -Xmx256m" -e "discovery.typesingle-node" -v /home/206/es7/data/:/usr/share/elasticsearch/data -p 9200:9200 -p 9300:9300 elasticsearch:7.14.0 …...
问题与分类
设计问题 是否已经有类似的解决方案,是否需要当前的设计设计思路的文档话,背景-》 设计思路-》 好处与不足 -》 其他设计思路的对比(淘汰其他设计思路的原因) 设计思路的评审,如何评审,如何量化ÿ…...
021-Qt 配置GitHub Copilot
Qt 配置GitHub Copilot 文章目录 Qt 配置GitHub Copilot项目介绍 GitHub Copilot配置 GitHub CopilotQt 前置条件升级QtGitHub Copilot 前置条件激活的了GitHub Copilot账号安装 Neovim 启用插件,重启Qt配置 GitHub Copilo安装Nodejs下载[copilot.vim](https://gith…...
如何使用 PostgreSQL 进行数据迁移和整合?
PostgreSQL 是一个强大的开源关系型数据库管理系统,它提供了丰富的功能和灵活性,使其成为许多企业和开发者的首选数据库之一。在开发过程中,经常会遇到需要将数据从一个数据库迁移到另一个数据库,或者整合多个数据源的情况。…...
Qt Signals Slots VS QEvents - Qt跨线程异步操作性能测试与选取建议
相关代码参考:https://gitcode.net/coloreaglestdio/qtcpp_demo/-/tree/master/qt_event_signal 1.问题的由来 在对 taskBus 进行低延迟改造时,避免滥用信号与槽起到了较好的作用。笔者在前一篇文章中,叙述了通过避免广播式地播发信号&…...
Postgres 和 MySQL 应该怎么选?
PostgreSQL和MySQL是两个流行的关系型数据库管理系统(DBMS)。它们都具有一些相似的功能,但也有一些区别。 在选择使用哪个DBMS时,需要考虑多个因素,包括性能、可扩展性、安全性、功能丰富度、生态系统支持等。下面是对…...
【在英伟达nvidia的jetson-orin-nx和PC电脑ubuntu20.04上-装配ESP32开发调试环境-基础测试】
【在英伟达nvidia的jetson-orin-nx和PC电脑ubuntu20.04上-装配ESP32开发调试环境-基础测试】 1、概述2、实验环境3、 物品说明4、参考资料与自我总结5、实验过程1、创建目录2、克隆下载文件3、 拉取子目录安装和交叉编译工具链等其他工具4、添加环境变量6、将样例文件拷贝到桌面…...
MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...
C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...
Android15默认授权浮窗权限
我们经常有那种需求,客户需要定制的apk集成在ROM中,并且默认授予其【显示在其他应用的上层】权限,也就是我们常说的浮窗权限,那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...
IT供电系统绝缘监测及故障定位解决方案
随着新能源的快速发展,光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域,IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选,但在长期运行中,例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...
(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...
什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南
文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果
引言 在人工智能飞速发展的今天,大语言模型(Large Language Models, LLMs)已成为技术领域的焦点。从智能写作到代码生成,LLM 的应用场景不断扩展,深刻改变了我们的工作和生活方式。然而,理解这些模型的内部…...
