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

C++:二叉搜索树的原理和模拟实现

文章目录

  • 二叉搜索树
    • 二叉搜索树的基本实现原理
  • 二叉搜索树的实现
    • 非递归版本的实现
    • 递归版本的实现

二叉搜索树

二叉搜索树也叫做二叉排序树,可以是空树,也可以是满足一些要求的二叉树

  1. 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  2. 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  3. 它的左右子树也分别为二叉搜索树

对于一种数据结构来说,大概率是实现增删查改这四个基本功能,这里实现的是增删查,对于改不实现的原因后续解释:

二叉搜索树的基本实现原理

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. 删除

二叉搜索树的删除较为复杂,下面分几种情况来进行讨论:

  1. 左根或右根为空

在这里插入图片描述
由于这种情况下最多只有一边有值,因此直接删除这个节点即可,令这个节点的父亲节点指向它的下一个节点

  1. 如果两边都有分支

解决的方法是,从要删除的这个节点的右子树中寻找一个可以替换它位置的数,这个数在寻找的时候选取的是右子树中的最小值,也就是右子树中的最左边的值就是所需要的值,交换后依旧可以满足二叉搜索树的条件,因此可以这样选择

	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++:二叉搜索树的原理和模拟实现

文章目录 二叉搜索树二叉搜索树的基本实现原理 二叉搜索树的实现非递归版本的实现递归版本的实现 二叉搜索树 二叉搜索树也叫做二叉排序树&#xff0c;可以是空树&#xff0c;也可以是满足一些要求的二叉树 若它的左子树不为空&#xff0c;则左子树上所有节点的值都小于根节点…...

学习视觉CV Transformer (2)--Transformer原理及代码分析

下面结合代码和原理进行深入分析Transformer原理。 2 Transformer深入分析 对于CV初学者来说&#xff0c;其实只需要理解Q K V 的含义和注意力机制的三个计算步骤&#xff1a; Q 和所有 K 计算相似性&#xff1b;对相似性采用 Softmax 转化为概率分布&#xff1b;将概率分布…...

【AI视野·今日CV 计算机视觉论文速览 第271期】Thu, 19 Oct 2023

AI视野今日CS.CV 计算机视觉论文速览 Thu, 19 Oct 2023 Totally 63 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Computer Vision Papers Learning from Rich Semantics and Coarse Locations for Long-tailed Object Detection Authors Lingchen Meng, Xiyang D…...

GoLong的学习之路(四)语法之循环语句

书接上回&#xff0c;上回说到运算符&#xff0c;这次我们说一个编程语言中最重要的一点&#xff1a;流程控制&#xff0c;及循环语句 文章目录 循环语句if else(分支结构)if条件判断特殊写法 for(循环结构)for range(键值循环) switch casegoto(跳转到指定标签)break(跳出循环…...

【Lua语法】字符串

Lua语言中的字符串是不可变值。不能像在C语言中那样直接改变某个字符串中的某个字符&#xff0c;但是可以通过创建一个新字符串的方式来达到修改的目的 print(add2(1 , 2 ,15,3))a "no one"b string.gsub(a , "no" , "on1111")print(a) print…...

程序员节的由来

早在2006年的时候 我就发现了 1024KB1MB 然后恰好又是2的10次方 那时候我就把这一天定义为程序员节了 不过当时并没有太多的知名度。 所以严格意义来讲 距历史记载&#xff0c;程序员应该是由我&#xff08;田尚滨/cagy&#xff09;发明的。 As early as 2006 I found …...

订水商城H5实战教程-03用户协议

目录 1 创建页面2 为文本组件增加事件3 检查用户协议是否勾选最终效果 我们上一篇介绍了打开首页时弹出登录窗口的功能&#xff0c;本篇我们实现一下用户协议。 1 创建页面 功能是点击用户协议的时候打开具体的协议内容&#xff0c;需要先创建一个页面。打开自定义应用&#x…...

淘宝app商品详情源数据API接口(解决滑块问题)可高并发采集

通过API接口采集淘宝商品列表和app商品详情遇到滑块验证码的解决方法&#xff08;带SKU和商品描述&#xff0c;支持高并发&#xff09;&#xff0c;主要是解决了高频情况下的阿里系滑块和必须要N多小号才能解决的反扒问题&#xff0c;以后都可以使用本方法&#xff1a; 大家都…...

xcode15一直显示正在连接iOS17真机问题解决

前言 更新xcode15之后&#xff0c;出现了各种报错问题&#xff0c;可谓是一路打怪啊&#xff0c;解决一个报错问题又来一个。没想到到了最后还能出现一个一直显示正在连接iOS17真机的问题 一直显示正在连接iOS17真机的问题 问题截图如下&#xff1a; 解决方法 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 实际开发过程中&#xff0c;页面数据往往是异步加载&#xff0c;接口请求回来后&#xff0c;数据刷新显示到界面上。 由于Flutter性能原因&#xff0c;也可能因为获取数据量比较大&#xff0c;在新页面路由进场动画执行过程中&#xff0c;接口请求结果回来了&#x…...

1024勋章

&#x1f338;关于重阳节的一些发疯日常&#xff08;昨天的聊天记录&#xff0c;今天发系列&#xff09;&#x1f605; &#x1f338;没错&#xff0c;发出来单纯觉得好玩儿&#x1f609;(为了1024勋章&#x1f60f;)芜湖&#xff01;...

C++栈、队列、优先级队列模拟+仿函数

目录 一、栈的模拟和deque容器 1.deque 1.1deque结构 1.2deque优缺点 2.stack模拟 二、队列的模拟 三、priority_queue优先级队列 1.优先级队列模拟 2.添加仿函数 一、栈的模拟和deque容器 在之前&#xff0c;我们学过了C语言版本的栈&#xff0c;可以看这篇文章 栈和…...

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 …...

问题与分类

设计问题 是否已经有类似的解决方案&#xff0c;是否需要当前的设计设计思路的文档话&#xff0c;背景-》 设计思路-》 好处与不足 -》 其他设计思路的对比&#xff08;淘汰其他设计思路的原因&#xff09; 设计思路的评审&#xff0c;如何评审&#xff0c;如何量化&#xff…...

021-Qt 配置GitHub Copilot

Qt 配置GitHub Copilot 文章目录 Qt 配置GitHub Copilot项目介绍 GitHub Copilot配置 GitHub CopilotQt 前置条件升级QtGitHub Copilot 前置条件激活的了GitHub Copilot账号安装 Neovim 启用插件&#xff0c;重启Qt配置 GitHub Copilo安装Nodejs下载[copilot.vim](https://gith…...

如何使用 PostgreSQL 进行数据迁移和整合?

​ PostgreSQL 是一个强大的开源关系型数据库管理系统&#xff0c;它提供了丰富的功能和灵活性&#xff0c;使其成为许多企业和开发者的首选数据库之一。在开发过程中&#xff0c;经常会遇到需要将数据从一个数据库迁移到另一个数据库&#xff0c;或者整合多个数据源的情况。…...

Qt Signals Slots VS QEvents - Qt跨线程异步操作性能测试与选取建议

相关代码参考&#xff1a;https://gitcode.net/coloreaglestdio/qtcpp_demo/-/tree/master/qt_event_signal 1.问题的由来 在对 taskBus 进行低延迟改造时&#xff0c;避免滥用信号与槽起到了较好的作用。笔者在前一篇文章中&#xff0c;叙述了通过避免广播式地播发信号&…...

Postgres 和 MySQL 应该怎么选?

PostgreSQL和MySQL是两个流行的关系型数据库管理系统&#xff08;DBMS&#xff09;。它们都具有一些相似的功能&#xff0c;但也有一些区别。 在选择使用哪个DBMS时&#xff0c;需要考虑多个因素&#xff0c;包括性能、可扩展性、安全性、功能丰富度、生态系统支持等。下面是对…...

【在英伟达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、将样例文件拷贝到桌面…...

深入剖析AI大模型:大模型时代的 Prompt 工程全解析

今天聊的内容&#xff0c;我认为是AI开发里面非常重要的内容。它在AI开发里无处不在&#xff0c;当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗"&#xff0c;或者让翻译模型 "将这段合同翻译成商务日语" 时&#xff0c;输入的这句话就是 Prompt。…...

【解密LSTM、GRU如何解决传统RNN梯度消失问题】

解密LSTM与GRU&#xff1a;如何让RNN变得更聪明&#xff1f; 在深度学习的世界里&#xff0c;循环神经网络&#xff08;RNN&#xff09;以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而&#xff0c;传统RNN存在的一个严重问题——梯度消失&#…...

[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...

uniapp微信小程序视频实时流+pc端预览方案

方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度​WebSocket图片帧​定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐​RTMP推流​TRTC/即构SDK推流❌ 付费方案 &#xff08;部分有免费额度&#x…...

Unity VR/MR开发-VR开发与传统3D开发的差异

视频讲解链接&#xff1a;【XR马斯维】VR/MR开发与传统3D开发的差异【UnityVR/MR开发教程--入门】_哔哩哔哩_bilibili...

使用python进行图像处理—图像滤波(5)

图像滤波是图像处理中最基本和最重要的操作之一。它的目的是在空间域上修改图像的像素值&#xff0c;以达到平滑&#xff08;去噪&#xff09;、锐化、边缘检测等效果。滤波通常通过卷积操作实现。 5.1卷积(Convolution)原理 卷积是滤波的核心。它是一种数学运算&#xff0c;…...

简约商务通用宣传年终总结12套PPT模版分享

IOS风格企业宣传PPT模版&#xff0c;年终工作总结PPT模版&#xff0c;简约精致扁平化商务通用动画PPT模版&#xff0c;素雅商务PPT模版 简约商务通用宣传年终总结12套PPT模版分享:商务通用年终总结类PPT模版https://pan.quark.cn/s/ece1e252d7df...

vxe-table vue 表格复选框多选数据,实现快捷键 Shift 批量选择功能

vxe-table vue 表格复选框多选数据&#xff0c;实现快捷键 Shift 批量选择功能 查看官网&#xff1a;https://vxetable.cn 效果 代码 通过 checkbox-config.isShift 启用批量选中,启用后按住快捷键和鼠标批量选取 <template><div><vxe-grid v-bind"gri…...

2025 后端自学UNIAPP【项目实战:旅游项目】7、景点详情页面【完结】

1、获取景点详情的请求【my_api.js】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口&#xff08;适配服务端返回 Token&#xff09; export const login async (code, avatar) > {const res await http(/login/getWXSessionKey, {code,avatar}); };//…...

linux设备重启后时间与网络时间不同步怎么解决?

linux设备重启后时间与网络时间不同步怎么解决&#xff1f; 设备只要一重启&#xff0c;时间又错了/偏了&#xff0c;明明刚刚对时还是对的&#xff01; 这在物联网、嵌入式开发环境特别常见&#xff0c;尤其是开发板、树莓派、rk3588 这类设备。 解决方法&#xff1a; 加硬件…...