【C++笔记】数据结构进阶之二叉搜索树(BSTree)

【C++笔记】数据结构进阶之二叉搜索树(BSTree)

【C++笔记】数据结构进阶之二叉搜索树(BSTree)

🔥个人主页大白的编程日记

🔥专栏C++笔记


文章目录

  • 【C++笔记】数据结构进阶之二叉搜索树(BSTree)
    • 前言
    • 一.二叉搜索树的概念
    • 二.二叉搜索树的性能分析
    • 三.二叉搜索树的实现
      • 3.1二叉树的中序遍历
      • 3.2二叉搜索树的插入
      • 3.3二叉搜索树的查找
      • 3.4二叉搜索树的删除
    • 四.二叉搜索树key和key/value使用场景\
      • 4.1key搜索场景
      • 4.2key/value搜索场景
      • 4.3key_value结构的实现
      • 4.4二叉搜索树的拷贝构造和析构
    • 后言

前言

哈喽,各位小伙伴大家好!上期我们讲了C++三大特性之多态。今天我们来讲一下二叉搜索树(BSTree)。话不多说,我们进入正题!向大厂冲锋

一.二叉搜索树的概念

二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:
• 若它的左子树不为空,则左子树上所有结点的值都小于等于根结点的值
• 若它的右子树不为空,则右子树上所有结点的值都⼤于等于根结点的值
• 它的左右子树也分别为二叉搜索树
• ⼆叉搜索树中可以支持插入相等的值,也可以不支持插人相等的值,具体看使用场景定义,后续我们学习map/set/multimap/multiset系列容器底层就是二叉搜索树,其中map/set不之持插入相等值,multimap/multiset支持插如相等值

在这里插入图片描述

二.二叉搜索树的性能分析

在这里插入图片描述

二叉搜索树最多查找高度次。因此二叉搜索树的性能取决的树的高度。

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

在这里插入图片描述


最优情况
最优情况下,二叉搜索树为完全⼆叉树(或者接近完全⼆叉树),其高度为: log2 N

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

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

  • 二分查找
    另外需要说明的是,二分查找也可以实现 O(log2 N) 级别的查找效率,但是而分查找有两大缺陷:
  1. 需要存储在支持下标随机访问的结构中,并且有序。
  2. 插入和删除数据效率很低,因为存储在下标随机访问的结构中,插入和删除数据⼀般需要挪动数据。

这里也就体现出了平衡⼆叉搜索树的价值。

三.二叉搜索树的实现

3.1二叉树的中序遍历


因为_root是私有成员,所以我们可以这样多套一层调用。

voidInorder(){_Inorder(_root); cout << endl;}void_Inorder(const node* root){if(root ==nullptr){return;}_Inorder(root->left); cout << root->_key <<" ";_Inorder(root->right);}

3.2二叉搜索树的插入

插入的具体过程如下:

  1. 树为空,则直接新增结点,赋值给root指针
  2. 树不空,按二叉搜索树性质,插入值比当前结点大往右走,插入值比当前结点小往左左,找到空位置,插入新结点。
  3. 如果支持插入相等的值,插入值跟当前结点相等的值可以往右走,也可以往左走,找到空位置,插入新结点。(要注意的是要保持逻辑⼀致性,插入相等的值不要⼀会往右走,⼀会往左走)
boolInsert(const k& x){if(_root ==nullptr)//插入根节点{ _root =newnode(x);returntrue;} node* cur = _root; node* parent =nullptr;//记录父亲节点while(cur){//介质不冗余/*if (x < cur->_key) { parent = cur; cur = cur->left; } else if (x > cur->_key) { parent = cur; cur = cur->right; } else { return false; }*///介质冗余if(x <= cur->_key)//相等插入左子树{ parent = cur; cur = cur->left;}elseif(x > cur->_key){ parent = cur; cur = cur->right;}}if(x > parent->_key){ parent->right =newnode(x);}else//相等插入左子树{ parent->left =newnode(x);}returntrue;}

介质冗余:


介质不冗余:

在这里插入图片描述

3.3二叉搜索树的查找

在这里插入图片描述
boolFind(const k& x){ node* cur = _root;while(cur){if(x < cur->_key)//节点在左子树{ cur = cur->left;}elseif(x > cur->_key)//节点在右子树{ cur = cur->right;}else{returntrue;//找到target}}returnfalse;}

3.4二叉搜索树的删除

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

  • 要删除结点N左右孩子均为空
  • 要删除的结点N左孩子位空,右孩子结点不为空
  • 要删除的结点N右孩子位空,左孩子结点不为空
  • 要删除的结点N左右孩子结点均不为空

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

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

左右均不空
无法直接删除N结点,因为N的两个孩子无处安放,只能用替换法删除。找N左子树的值最大结点R(最右结点)或者N右子树的值最小结点R(最左结点)替代N,因为这两个结点中任意⼀个,放到N的位置,都满足⼆叉搜索树的规则。替代N的意思就是N和R的两个结点的值交换,转而变成删除R结点,R结点符合情况2或情况3,可以直接删除。

在这里插入图片描述


右空左不空
把N结点的父亲对应孩子指针指向N的左孩子,直接删除N结点

在这里插入图片描述



如果删除根节点直接修改_root。再删除节点即可。

左空右不空
把N结点的父亲对应孩子指针指向N的右孩子,直接删除N结点

boolErase(const k& x){ node* cur = _root; node* parent =nullptr;while(cur){if(x < cur->_key){ parent = cur; cur = cur->left;}elseif(x > cur->_key){ parent = cur; cur = cur->right;}else//找到删除节点{if(cur->left ==nullptr)//左为空{if(cur == _root)//防止删除根节点{ _root = cur->right;}else//判断连接父亲的左子树还是右子树{if(cur == parent->left){ parent->left = cur->right;}else{ parent->right = cur->right;}}delete cur;returntrue;}elseif(cur->right ==nullptr){if(cur == _root)//防止删除根节点{ _root = cur->left;}else//判断连接父亲的左子树还是右子树{if(cur == parent->left){ parent->left = cur->left;}else{ parent->right = cur->left;}}delete cur;returntrue;}else{//替代节点的父亲初始化为删除节点防止不进去循环 node* replaceparent = cur;//替代节点的父亲 node* replace = cur->right;while(replace->left)//寻找右子树的最小节点{ replaceparent = replace; replace = replace->left;} cur->_key = replace->_key;//替换根节点//判断连接父亲的左子树还是右子树if(replace == replaceparent->left){ replaceparent->left = replace->right;}else{ replaceparent->right = replace->right;}delete replace;returntrue;}}}returnfalse;}

四.二叉搜索树key和key/value使用场景\

4.1key搜索场景

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

场景1:小区无人值守车库,小区车库买了车位的业主车才能进小区,那么物业会把买了车位的业主的车牌号录入后台系统,车辆进入时扫描车牌在不在系统中,在则抬杆,不在则提示非本小区车辆,无法进入。

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

4.2key/value搜索场景

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

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

场景2:商场无人值守车库,入口进场时扫描车牌,记录车牌和入场时间,出口离场时,扫描车牌,查找入场时间,⽤当前时间-入场时间计算出停车时长,计算出停车费用,缴费后抬杆,车辆离场。

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

4.3key_value结构的实现

  • 结构的定义
    把节点封装,key_value多了一个V的类型。
    同时这里using可以当成typedef使用。
//key结构template<classk>structBSTNode{using node = BSTNode<k>; k _key; node* left; node* right;BSTNode(const k& key):_key(key),left(nullptr),right(nullptr){}};template<classk>classBSTree{using node = BSTNode<k>;public:private: node* _root =nullptr;};//key_value结构template<classk,classv>structBSTNode{using node = BSTNode<k, v>; k _key; v _value; node* left; node* right;BSTNode(const k& key,const v& value):_key(key),_value(value),left(nullptr),right(nullptr){}};template<classk,classv>classBSTree{using node = BSTNode<k, v>;public:private: node* _root =nullptr;};
  • 查找和删除
    查找和删除的逻辑不需要改变。但是如果想知道查找的节点的value可以返回节点指针。同时如果允许介质冗余的话,那就需要查找中序的第一个,所以我们相同值时,先保留,继续去左子树查找即可。
node*Find(const k& x){ node* ret =nullptr; node* cur = _root;while(cur){if(x < cur->_key){ cur = cur->left;}elseif(x > cur->_key){ cur = cur->right;}else{ ret = cur;//保留当前节点 cur = cur->left;//继续向左子树查找中序的第一个}}return ret;} 、、key_value结构 
  • 插入

插入只需要多插入一个value值即可,逻辑不变。

boolInsert(const k& x,const v& v){if(_root ==nullptr)//插入根节点{ _root =newnode(x, v);returntrue;} node* cur = _root; node* parent =nullptr;//保留父亲节点while(cur){/*介质不冗余*/if(x < cur->_key){ parent = cur; cur = cur->left;}elseif(x > cur->_key){ parent = cur; cur = cur->right;}else{returnfalse;}//介质冗余//if (x <= cur->_key)//相等插入到左子树//{// parent = cur;// cur = cur->left;//}//else if (x > cur->_key)//{// parent = cur;// cur = cur->right;//}}if(x > parent->_key){ parent->right =newnode(x, v);}else//相等插入左子树{ parent->left =newnode(x, v);}returntrue;}

4.4二叉搜索树的拷贝构造和析构

  • 拷贝构造

直接new构造根节点,再让根节点连接递归构造的左右子树即可。注意如果按照插入的思路构造,那就必须是层序遍历节点插入,否则拷贝出来的子树就不一样。

BSTree(const BSTree<k,v>& x){ _root =Copy(x._root);} node*Copy( node* x){if(x ==nullptr){return x;} node* root =newnode(x->_key, x->_value); root->left =Copy(x->left); root->right =Copy(x->right);return root;}
  • 析构
    这里先析构左右子树在析构根节点即可。
voidDestory(const node* root){if(root ==nullptr){return;}Destory(root->left);Destory(root->right);delete root;}~BSTree(){Destory(_root); _root =nullptr;}
  • 赋值

这里先拷贝tmp,然后交换_root指针,函数结束后析构tmp空间。

BSTree<k, v>&operator=(BSTree<k, v> tmp){swap(_root,tmp._root);return*this;}


后言

这就是数据结构进阶之二叉搜索树(BSTree)。大家自己好好消化!今天就分享到这!感谢各位的耐心垂阅!咱们下期见!拜拜~

Read more

假网站排全网第二,真官网翻五页都找不到!NanoClaw创始人破防:SEO之战,我快要输了

假网站排全网第二,真官网翻五页都找不到!NanoClaw创始人破防:SEO之战,我快要输了

整理 | 苏宓 出品 | ZEEKLOG(ID:ZEEKLOGnews) 自从 OpenClaw 爆火之后,各种“Claw”项目接连出现,其中以安全优化版 NanoClaw 最为知名。它的核心代码仅有 4000 行,却获得了 AI 大牛 Andrej Karpathy 的点赞。 可谁也没想到,这款口碑极佳的开源项目,近来竟被一个仿冒网站抢了风头。 投诉无门之下,NanoClaw 创始人 Gavriel Cohen 在 X 社交平台上无奈发文怒斥:谷歌搜索错误地将假网站排在真官网前面,不仅破坏了项目声誉,还埋下了严重的安全隐患,而他费尽心力,却只能哀叹一句——“我正在为自己的开源项目打 SEO 战,但我快要输了。” 那么,NanoClaw 究竟发生了什么?又是怎么走红的?事情还要从 OpenClaw

By Ne0inhk
曝Windows 12将于今年发布?以AI为核心、NPU成「硬件门槛」,网友吐槽:“不想要的全塞进来了”

曝Windows 12将于今年发布?以AI为核心、NPU成「硬件门槛」,网友吐槽:“不想要的全塞进来了”

整理 | 郑丽媛 出品 | ZEEKLOG(ID:ZEEKLOGnews) 当年,微软一句“Windows 10 将是最后一个版本”的表态,让不少用户以为 Windows 进入了“只更新、不换代”的时代。但几年过去,现实却完全不同。 在 Windows 11 发布之后,如今关于 Windows 12 的传闻再次密集出现。从内部代号、代码片段,到硬件厂商的暗示与 OEM 预热标签,种种线索拼在一起,勾勒出一个明显的趋势——这不会只是一次常规升级,而更像是一次围绕 AI 的平台级重构。 更关键的是,这次争议,可能远比当年 TPM 2.0 更大。 精准卡位 Windows 10 退场的时间?

By Ne0inhk
Python热度下滑、AI能取代搜索引擎?TIOBE最新榜单揭晓!

Python热度下滑、AI能取代搜索引擎?TIOBE最新榜单揭晓!

整理 | 屠敏 出品 | ZEEKLOG(ID:ZEEKLOGnews) 日前,TIOBE 发布了最新的 3 月编程语言榜单。整体来看,本月排名变化不算大,但榜单中仍然出现了一些值得关注的小波动。  AI 工具能帮大家秒懂最新编程语言趋势? 由于 2 月天数较少,3 月的榜单整体变化有限。借着这次发布,TIOBE CEO Paul Jansen 也回应了一个最近被频繁讨论的问题:为什么 TIOBE 指数仍然依赖搜索引擎统计结果?在大语言模型流行的今天,直接询问 AI 哪些编程语言最流行,是不是更简单? 对此,Jansen 的回答是否定的。 他解释称,TIOBE 指数本质上统计的是互联网上关于某种编程语言的网页数量。而大语言模型的训练数据同样来自这些网页内容,因此从信息来源来看,两者并没有本质区别。换句话说,LLM 的判断,本质上也是建立在这些网页数据之上的。 Python 活跃度仍在下降

By Ne0inhk
“裸奔龙虾”数量已达27万只,业内人士警告;AI浪潮下,中传“砍掉”翻译等16个专业;薪资谈判破裂,三星电子8.9万人要罢工 | 极客头条

“裸奔龙虾”数量已达27万只,业内人士警告;AI浪潮下,中传“砍掉”翻译等16个专业;薪资谈判破裂,三星电子8.9万人要罢工 | 极客头条

「极客头条」—— 技术人员的新闻圈! ZEEKLOG 的读者朋友们好,「极客头条」来啦,快来看今天都有哪些值得我们技术人关注的重要新闻吧。(投稿或寻求报道:[email protected]) 整理 | 郑丽媛 出品 | ZEEKLOG(ID:ZEEKLOGnews) 一分钟速览新闻点! * “裸奔龙虾”已高达27万只!业内人士警告:一旦黑客入侵,敏感信息一秒搬空 * 阿里云 CTO 周靖人代管千问模型一号位,刘大一恒管理更多团队 * 中国传媒大学砍掉翻译、摄影等 16 个本科专业,直言教育要面向人机分工时代 * 雷军放话:小米将很快推出 L3、L4 的驾驶 * 消息称原理想汽车智驾一号位郎咸朋具身智能赛道创业 * vivo 前产品经理宋紫薇创业,瞄准 AI 时尚Agent,获亿元融资 * MiniMax 发布龙虾新技能,股价暴涨超 23% * 薪资谈判破裂,三星电子

By Ne0inhk