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

TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...

Debian系统简介
目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版ÿ…...

自然语言处理——Transformer
自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效,它能挖掘数据中的时序信息以及语义信息,但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN,但是…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)
UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中,UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化…...
MySQL JOIN 表过多的优化思路
当 MySQL 查询涉及大量表 JOIN 时,性能会显著下降。以下是优化思路和简易实现方法: 一、核心优化思路 减少 JOIN 数量 数据冗余:添加必要的冗余字段(如订单表直接存储用户名)合并表:将频繁关联的小表合并成…...
深入理解Optional:处理空指针异常
1. 使用Optional处理可能为空的集合 在Java开发中,集合判空是一个常见但容易出错的场景。传统方式虽然可行,但存在一些潜在问题: // 传统判空方式 if (!CollectionUtils.isEmpty(userInfoList)) {for (UserInfo userInfo : userInfoList) {…...

nnUNet V2修改网络——暴力替换网络为UNet++
更换前,要用nnUNet V2跑通所用数据集,证明nnUNet V2、数据集、运行环境等没有问题 阅读nnU-Net V2 的 U-Net结构,初步了解要修改的网络,知己知彼,修改起来才能游刃有余。 U-Net存在两个局限,一是网络的最佳深度因应用场景而异,这取决于任务的难度和可用于训练的标注数…...

何谓AI编程【02】AI编程官网以优雅草星云智控为例建设实践-完善顶部-建立各项子页-调整排版-优雅草卓伊凡
何谓AI编程【02】AI编程官网以优雅草星云智控为例建设实践-完善顶部-建立各项子页-调整排版-优雅草卓伊凡 背景 我们以建设星云智控官网来做AI编程实践,很多人以为AI已经强大到不需要程序员了,其实不是,AI更加需要程序员,普通人…...

DeepSeek越强,Kimi越慌?
被DeepSeek吊打的Kimi,还有多少人在用? 去年,月之暗面创始人杨植麟别提有多风光了。90后清华学霸,国产大模型六小虎之一,手握十几亿美金的融资。旗下的AI助手Kimi烧钱如流水,单月光是投流就花费2个亿。 疯…...
python读取SQLite表个并生成pdf文件
代码用于创建含50列的SQLite数据库并插入500行随机浮点数据,随后读取数据,通过ReportLab生成横向PDF表格,包含格式化(两位小数)及表头、网格线等美观样式。 # 导入所需库 import sqlite3 # 用于操作…...