C++进阶:二叉搜索树

✨✨所属专栏:C++✨✨

✨✨作者主页:嶔某✨✨

 ⼆叉搜索树的概念

⼆叉搜索树⼜称⼆叉排序树,它或者是⼀棵空树,或者是具有以下性质的⼆叉树:  

• 若它的左⼦树不为空,则左⼦树上所有结点的值都⼩于等于根结点的值

• 若它的右⼦树不为空,则右⼦树上所有结点的值都⼤于等于根结点的值

• 它的左右⼦树也分别为⼆叉搜索树

template<class K>
struct BSNode
{BSNode(const K& key){_key = key;_left = nullptr;_right = nullptr;}struct BSNode* _left;struct BSNode* _right;K _key;
};
template<class K>
class BSTree
{typedef struct BSNode<K> Node;
public:// 函数
private:Node* _root = nullptr;
};

⼆叉搜索树中可以⽀持插⼊相等的值,也可以不⽀持插⼊相等的值,具体看使⽤场景定义,后续学习map/set/multimap/multiset系列容器底层就是⼆叉搜索树,其中map/set不⽀持插⼊相等值,multimap/multiset⽀持插⼊相等值。

⼆叉搜索树的性能分析

最优情况下,⼆叉搜索树为完全⼆叉树(或者接近完全⼆叉树),其⾼度为: O(lgN) 

最差情况下,⼆叉搜索树退化为单⽀树(或者类似单⽀),其⾼度为: O(N/2) 

所以综合⽽⾔⼆叉搜索树增删查改时间复杂度为: O(N)

那么这样的效率显然是⽆法满⾜我们需求的,后续需要继续了解⼆叉搜索树的变形,平衡⼆叉搜索树AVL树和红⿊树,才能适⽤于我们在内存中存储和搜索数据。

另外,⼆分查找也可以实现O(logN) 级别的查找效率,但是⼆分查找有两⼤缺陷:

1. 需要存储在⽀持下标随机访问的结构中,并且有序

2. 插⼊和删除数据效率很低,因为存储在下标随机访问的结构中,插⼊和删除数据⼀般需要挪动数 据。 这⾥也就体现出了平衡⼆叉搜索树的价值。 

⼆叉搜索树的插⼊

插⼊的具体过程如下:

1. 树为空,则直接新增结点,赋值给root指针

2. 树不空,按⼆叉搜索树性质,插⼊值⽐当前结点⼤往右⾛,插⼊值⽐当前结点⼩往左⾛,找到空位置,插⼊新结点。

3. 如果⽀持插⼊相等的值,插⼊值跟当前结点相等的值可以往右⾛,也可以往左⾛,找到空位置,插 ⼊新结点。(要注意的是要保持逻辑⼀致性,插⼊相等的值不要⼀会往右⾛,⼀会往左⾛) 

bool Insert(const K& key){Node* newnode = new Node(key);if (_root == nullptr){_root = newnode;return true;}else{Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_key > newnode->_key){parent = cur;cur = cur->_left;}else if (cur->_key < newnode->_key){parent = cur;cur = cur->_right;}elsereturn false;}if (parent->_key > newnode->_key)parent->_left = newnode;if (parent->_key < newnode->_key)parent->_right = newnode;return true;}}

⼆叉搜索树的查找

1. 从根开始⽐较,查找x,x⽐根的值⼤则往右边⾛查找,x⽐根值⼩则往左边⾛查找。

2. 最多查找⾼度次,⾛到到空,还没找到,这个值不存在。

3. 如果不⽀持插⼊相等的值,找到x即可返回

4. 如果⽀持插⼊相等的值,意味着有多个x存在,⼀般要求查找中序的第⼀个x。如下图,查找3,要找到1的右孩⼦的那个3返回 

Node* Find(const K& key){assert(_root);Node* cur = _root;while (cur){if (cur->_key > key){cur = cur->_left;}else if (cur->_key < key){cur = cur->_right;}elsereturn cur;}return nullptr;}

 ⼆叉搜索树的删除

⾸先查找元素是否在⼆叉搜索树中,如果不存在,则返回false。 如果查找元素存在则分以下四种情况分别处理:(假设要删除的结点为N)

1. 要删除结点N左右孩⼦均为空

2. 要删除的结点N左孩⼦为空,右孩⼦结点不为空

3. 要删除的结点N右孩⼦为空,左孩⼦结点不为空

注意还有这种情况:

4. 要删除的结点N左右孩⼦结点均不为空

对应以上四种情况的解决⽅案:

1. 把N结点的⽗亲对应孩⼦指针指向空,直接删除N结点(情况1可以当成2或者3处理,效果是⼀样的)

2. 把N结点的⽗亲对应孩⼦指针指向N的右孩⼦,直接删除N结点

3. 把N结点的⽗亲对应孩⼦指针指向N的左孩⼦,直接删除N结点

4. ⽆法直接删除N结点,因为N的两个孩⼦⽆处安放,只能⽤替换法删除。找N左⼦树的值最⼤结点 R(最右结点)或者N右⼦树的值最⼩结点R(最左结点)替代N,因为这两个结点中任意⼀个,放到N的位置,都满⾜⼆叉搜索树的规则。替代N的意思就是N和R的两个结点的值交换,转⽽变成删除R结点,R结点符合情况2或情况3,可以直接删除。

注意:删除的代码逻辑较为复杂,复习的时候最好再写一遍~ 

bool Erase(const K& key){assert(_root);Node* cur = _root;Node* parent = nullptr;while (cur){if (cur->_key > key){parent = cur;cur = cur->_left;}else if (cur->_key < key){parent = cur;cur = cur->_right;}else{// 删除if (cur->_left == nullptr){if (cur == _root)//如果删除没有左子树的根节点{_root = cur->_right;}// 左子树为空and左右子树都为空else if (parent->_left == cur){parent->_left = cur->_right;}else if (parent->_right == cur){parent->_right = cur->_right;}delete cur;}else if (cur->_right == nullptr){if (cur == _root)//如果删除没有右子树的根节点{_root = cur->_left;}// 右子树为空else if (parent->_left == cur){parent->_left = cur->_left;}else if (parent->_right == cur){parent->_right = cur->_left;}delete cur;}else{//左右子树都不为空Node* replaceparent = cur;Node* replace = cur->_right;while (replace->_left){replaceparent = replace;replace = replace->_left;}cur->_key = replace->_key;if (replaceparent->_left == replace){replaceparent->_left = replace->_right;}else if (replaceparent->_right == replace){replaceparent->_right = replace->_right;}delete replace;}return true;}}return false;}

 ⼆叉搜索树key和key/value使⽤场景

 key搜索场景:

只有key作为关键码,结构中只需要存储key即可,关键码即为需要搜索到的值,搜索场景只需要判断key在不在。key的搜索场景实现的⼆叉树搜索树⽀持增删查,但是不⽀持修改,修改key破坏搜索树结构了。

场景1:⼩区⽆⼈值守⻋库,⼩区⻋库买了⻋位的业主⻋才能进⼩区,那么物业会把买了⻋位的业主的⻋牌号录⼊后台系统,⻋辆进⼊时扫描⻋牌在不在系统中,在则抬杆,不在则提示⾮本⼩区⻋辆,⽆法进⼊。

场景2:检查⼀篇英⽂⽂章单词拼写是否正确,将词库中所有单词放⼊⼆叉搜索树,读取⽂章中的单词,查找是否在⼆叉搜索树中,不在则波浪线标红提示。

key/value搜索场景:

每⼀个关键码key,都有与之对应的值value,value可以任意类型对象。树的结构中(结点)除了需要存储key还要存储对应的value,增/删/查还是以key为关键字⾛⼆叉搜索树的规则进⾏⽐较,可以快速查找到key对应的value。key/value的搜索场景实现的⼆叉树搜索树⽀持修改,但是不⽀持修改key,修改key破坏搜索树结构了,可以修改value。

场景1:简单中英互译字典,树的结构中(结点)存储key(英⽂)和vlaue(中⽂),搜索时输⼊英⽂,则同时查找到了英⽂对应的中⽂。

场景2:商场⽆⼈值守⻋库,⼊⼝进场时扫描⻋牌,记录⻋牌和⼊场时间,出⼝离场时,扫描⻋牌,查找⼊场时间,⽤当前时间-⼊场时间计算出停⻋时⻓,计算出停⻋费⽤,缴费后抬杆,⻋辆离场。

场景3:统计⼀篇⽂章中单词出现的次数,读取⼀个单词,查找单词是否存在,不存在这个说明第⼀次出现,(单词,1),单词存在,则++单词对应的次数。 

本期博客到这里就结束了,如果有什么错误,欢迎指出,如果对你有帮助,请点个赞,谢谢!

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

tea 加密解密算法(面向ctf-reverse使用,光速学会tea逆向套路)

一&#xff0c;算法特征 tea算法的主要特征表现在sum和delta变量&#xff0c;以及3行核心加密中出现的右移4左移5&#xff0c;两行各有3个小括号互相异或 在题目中看到这些特征时就应该警醒这是tea相关算法 delta的值一般为0x9E3779B9(-0x61C88647)&#xff0c;但题目中往往…

深入了解字符函数和字符串函数

前言&#xff1a;今天给大家深入理解一下字符函数 和 字符串函数。通过使用 和 模拟实现 带大家加深理解&#xff0c;让大家灵活使用。 字符函数 在C语言中&#xff0c;有一系列函数是专门做字符分类的&#xff0c;也就是一个字符属于什么类型的字符。 这些函数的使用都要包含…

IDEA复制代码到MD笔记格式还手动调,赶紧试试这个功能,一步到位

你是否曾经有过这种复制代码到笔记代码块的经历&#xff0c;选中后代码左侧有一些空格 然后粘到Markdown笔记里除第一行外&#xff0c;其他几行都要手动向前缩进&#xff0c;真是逼死强迫症啊 但是&#xff0c;其实idea工具中有一个“列选择模式”的功能&#xff0c;我们可以…

初中生物--5.单细胞生物

单细胞生物 单细胞生物整个身体只由一个细胞构成&#xff0c;是生物 圈中非常原始&#xff0c;形态微小、结构简单的一类生物。大多数生活在水域或湿润的环境中&#xff0c;也有寄生在 其他生物身体上 例如&#xff1a;大肠杆菌、眼虫、酵母菌、变形虫、衣藻、草履虫 草履虫 …

微信视频号导出视频软件

最近研究了一下微信视频号导出视频的方法&#xff0c;目前发现还是比较难搞&#xff0c;查了一些资料&#xff0c;写了一个可以导出视频的软件&#xff0c;目前还不完善&#xff0c;但是导出视频到本地还是没问题&#xff0c;先用着吧&#xff0c;后期再完善。先记录一下。 测…

linux网络编程1

24.9.16学习目录 一.TCP/IP协议简介1.TCP/IP的分层结构2.协议的简介 二、MAC地址和IP地址1.网卡2.MAC地址3.IP地址&#xff08;1&#xff09;IP地址的分类&#xff08;2&#xff09;IP地址的特点&#xff08;3&#xff09;回环IP地址 3.子网掩码4.端口&#xff08;1&#xff09…

神经网络通俗理解学习笔记(5) 自然语言处理

自然语言处理 词嵌入和word2vec词义搜索和句意表示预训练模型Hugging Face库介绍经典NLP数据集代码案例-电影评论情感分析 词嵌入和word2vec 词嵌入是一种 将高维的数据表示映射到低维空间的方法 word embedding 是将语言中的词编码成向量便于后续的分析和处理 词嵌入和词向量…

JavaScript 事件处理

一、简介 ​ 事件&#xff1a;发生在HTML元素上的事情&#xff0c;可以是用户的行为&#xff0c;也可以是浏览器的行为&#xff0c;如 用户点击了某个HTML元素用户将鼠标移动到某个HTML元素上用户输入数据时光标离开页面加载完成 ​ 事件源&#xff1a;事件触发的源头&#xf…

Maya怎么把黑色的面反转为白色面

1、选中需要调整的面。 2、点击菜单栏中的“网格显示”&#xff0c;再点击点击“反转(Reverse)”。 3、反转后&#xff0c;原本黑色的面将会变成正常的面&#xff0c;法线方向也会相应改变。 按住ctrlshift鼠标中键 拖动快捷图标至工具栏

photozoom pro 9如何激活解锁 2024最新激活解锁代码

您好,现在程程来为大家解答以上的问题。photozoom pro 9解锁代码&#xff0c;photozoom pro 9解锁代码相信很多小伙伴还不知道,现在让我们一起来看... 您好,现在程程来为大家解答以上的问题。photozoom pro 9解锁代码&#xff0c;photozoom pro 9解锁代码相信很多小伙伴还不知道…

54.【C语言】 字符函数和字符串函数(strncpy,strncat,strncmp函数)

和strcpy,strcat,strcmp函数对应的是strncpy,strncat,strncmp函数 8.strncpy函数 *简单使用 cplusplus的介绍 点我跳转 翻译: 函数 strncpy char * strncpy ( char * destination, const char * source, size_t num ); 从字符串中复制一些字符 复制源(source)字符串的前num个…

LeetCode题集-4 - 寻找两个有序数组的中位数,图文并茂,六种解法,万字讲解

题目&#xff1a;给定两个大小分别为 m 和 n 的正序&#xff08;从小到大&#xff09;数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。 算法的时间复杂度应该为 O(log (mn)) 。 作为目前遇到的第一个困难级别题目&#xff0c;我感觉这题还是挺难的&#xff0c…

Linux常用命令以及操作技巧

&#x1f30f;个人博客主页&#xff1a;意疏-CSDN博客 希望文章能够给到初学的你一些启发&#xff5e; 如果觉得文章对你有帮助的话&#xff0c;点赞 关注 收藏支持一下笔者吧&#xff5e; 阅读指南&#xff1a; 开篇说明帮助命令常见的七个linux操作终端实用的技巧跟文件目录…

idea集成和使用Git指南

前言 Git是一个分布式的版本控制工具&#xff0c;可以管理我们开发过程中的源代码文件&#xff0c;而idea是Java的集成开发环境&#xff0c;在idea中配置Git&#xff0c;可以提高我们的团队开发效率。因此在idea中集成Git并使用Git管理我们的源代码是必要的 第一步&#xff1a;…

计算机毕业设计python校园物资招标投标竞标系统 w235g

目录 技术栈和环境说明具体实现截图预期达到的目标系统设计详细视频演示技术路线解决的思路性能/安全/负载方面可行性分析论证python-flask核心代码部分展示python-django核心代码部分展示研究方法感恩大学老师和同学源码获取 技术栈和环境说明 本系统以Python开发语言开发&am…

Java 入门指南:JVM(Java虚拟机)垃圾回收机制 —— 死亡对象判断方法

文章目录 垃圾回收机制死亡对象判断方法引用计数法可达性分析算法可以作为 GC Roots 的对象判断对象被回收需要经历的过程 引用类型引用汇总引用队列 废弃常量判定废弃常量废弃原因遵循原则 无用的类所需条件造成的问题解决步骤 垃圾回收机制 垃圾回收&#xff08;Garbage Col…

【探索数据结构与算法】插入排序:原理、实现与分析(图文详解)

目录 一、插入排序 算法思想 二、插入排序 算法步骤 四、复杂度分析 时间复杂度&#xff1a;O(n^2) 空间复杂度&#xff1a;O(1) 稳定性&#xff1a;稳定算法 五、应用场景 &#x1f493; 博客主页&#xff1a;C-SDN花园GGbond ⏩ 文章专栏&#xff1a;探索数据结构…

STM32之FMC—扩展外部 SDRAM

文章目录 一、FMC外设介绍二、SDRAM 控制原理1、SDRAM关键参数a、容量、分区b、引脚SDRAM 使用 2、SDRAM芯片IS42S16400J3、SDRAM 控制引脚说明控制逻辑地址控制SDRAM 的存储阵列SDRAM 的命令预充电刷新 W9825G6KH&#xff1a;W9825G6KH引脚 三、STM32F429 FMC四、其他文章打开…

基于ssm的个性化影片推荐系统设计与实现

需要项目源码请联系我&#xff0c;目前有各类成品 毕设 javaweb ssh ssm springboot等等项目框架&#xff0c;源码丰富。 专业团队&#xff0c;咨询就送开题报告&#xff0c;活动限时免费&#xff0c;有需要的朋友可以来咨询。 一、摘要 随着科学技术的飞速发展&#xff0c;社…

Matlab simulink建模与仿真 第十五章(信号源库)

参考视频&#xff1a;simulink1.1simulink简介_哔哩哔哩_bilibili 一、信号源库中的模块概览 注&#xff1a;部分模块在第二章中有介绍&#xff0c;本章不再赘述。 二、from输入源模块 1、From Workspace模块 &#xff08;1&#xff09;该模块可从MATLAB工作区、模型工作区…