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

【C++笔记】二叉搜索树

前言各位读者朋友们大家好上期我们讲完了面向对象编程三大属性之一的多态这一期我们再次开始数据结构二叉搜索树的讲解。目录前言一. 二叉搜索树的概念二. 二叉搜索树的性能分析三. 二叉搜索树的插入四. 二叉搜索树的查找五. 二叉搜索树的删除六. 二叉搜索树key和key/value使用场景6.1 key搜索场景6.2 key/value搜索场景七. 析构函数、拷贝构造以及赋值重载的实现结语一. 二叉搜索树的概念二叉搜索树又称二叉排序树它或者是一颗空树或者是具有以下性质的二叉树若它的左子树不为空则左子树上的所有节点的值都小于等于根节点的值若它的右子树不为空则右子树上的所有节点的值都大于等于根节点的值它的左右子树也是二叉搜索树二叉搜索树中可以支持插入相同的值也可以不支持插入相同的值具体看使用场景定义后续我们会学习map/set/multimap/multiset系列容器底层就是二叉搜索树其中map/set不支持插入相等的值multimap/multiset支持插入相等的值。二. 二叉搜索树的性能分析最优情况下二叉搜索树为完全二叉树或者接近于完全二叉树最多的搜索次数是高度次即O(logN)最坏情况下二叉树退化为单枝树或者类似于单枝树最坏的搜索次数是高度次即O(N)所以综合而言二叉搜索树的增删查改的时间复杂度是O(N)这样的效率显然是无法满足我们的需求的我们后面会继续讲解二叉搜索树的变形平衡搜索树AVL树和红黑树才能适合我们在内存中存储和搜索数据。另外需要说明的是二分查找也可以实现O(logN)的查找效率但是二分查找有两大缺陷需要存储在支持下标随机访问的结构中并且有序插入和删除数据的效率很低因为在支持下标随机访问的结构中插入和删除数据一般都需要挪动数据这里就体现出了平衡二叉搜索树的价值。三. 二叉搜索树的插入插入的具体过程如下如果树为空则直接新增节点赋值给root指针树不为空按二叉搜索树性质插入值比当前节点值大的往右走插入值比当前节点小的往左走找到空位置插入新节点如果支持插入相等的值插入值跟当前节点相等的值可以往左走也可以往右走找到空位置插入新节点。(要注意的是保持逻辑的一致插入相等的值不要一会往左走一会往右走)二叉搜索树的结构templateclassK// 节点structBTNode{K _key;BTNodeK*_left;BTNodeK*_right;BTNode(constKkey):_key(key),_left(nullptr),_right(nullptr){}};templateclassKclassBSTree{typedefBTNodeKNode;public:// 成员函数private:Node*_root;};二叉搜索树插入代码的实现boolInsert(constKkey){if(_rootnullptr){_rootnewNode(key);returntrue;}Node*parentnullptr;// 父亲节点Node*cur_root;// 当前节点while(cur){if(cur-_keykey)// 当前节点的值大于key往左走{parentcur;curcur-_left;}elseif(cur-_keykey)// 当前节点的值小于key往右走{parentcur;curcur-_right;}else{returnfalse;}}// 找到空位置curnewNode(key);if(parent-_keykey){parent-_leftcur;}else{parent-_rightcur;}returntrue;}四. 二叉搜索树的查找从根节点开始比较查找key, key比根节点的值大往右找比根结点的值小往左边找。最多查找高度次走到空如果还没有找到这个值就不存在如果不支持插入相等的值找到key返回即可如果支持插入相等的值意味着有多个key存在一般要求查找中序中的第一个key。二叉搜索树查找代码实现boolFind(constKkey){Node*cur_root;while(cur){if(cur-_keykey)// 往左走{curcur-_left;}elseif(cur-_keykey){curcur-_right;}else{returntrue;}}returnfalse;}五. 二叉搜索树的删除首先查找元素是否在二叉搜索树中如果不存在就返回false。如果查找的元素存在则分以下四种情况处理要删除的节点左右子树均为空要删除的节点的左子树为空右子树不为空要删除的节点的右子树为空左子树不为空要删除的节点左右子树均不为空对应以上四种情况的解决方法对于前三种比较好处理如果左子树为空右子树不为空就将该节点的父亲节点指向该节点的右子树删除该节点如果右子树为空左子树不为空就将该节点的父亲节点指向该节点的左子树然后删除该节点对于左右子树均为空树的情况上面的方法就可以解决需要注意的是在父亲节点连接子树的时候需要判断删除的节点是父亲节点的左子树还是右子树。对于左右子树均不为空的情况我们要直接删的话难度很大二叉搜索树的性质左右子树均是二叉树搜索树我们把这个节点删除之后还要保持这个性质所以可以将左子树最大的节点最右节点找到把最大节点的值赋值给要删除的节点然后连接上最大节点的左子树删除这个最大的节点或者找右子树中最小的节点最左节点重复上面的操作即可。boolErase(constKkey){Node*cur_root;Node*parentnullptr;// 找key节点while(cur){if(cur-_keykey){parentcur;curcur-_right;}elseif(cur-_keykey){parentcur;curcur-_left;}else// 找到key了{// 左为空父亲节点连右子树if(cur-_leftnullptr){if(cur_root){_rootcur-_right;}else{//当前节点是父亲节点的左子树if(curparent-_left){parent-_leftcur-_right;}else//当前节点是父亲节点的右子树{parent-_rightcur-_right;}}deletecur;}// 右子树为空父亲节点连左子树elseif(cur-_rightnullptr){if(cur_root){_rootcur-_left;}else{//当前节点是父亲节点的左子树if(curparent-_left){parent-_leftcur-_left;}else{parent-_rightcur-_left;}}deletecur;}else//左右子树都不为空找左子树中最大的(最右节点){Node*destcur-_left;Node*parentcur;while(dest-_right){parentdest;destdest-_right;}cur-_keydest-_key;if(parent-_leftdest)parent-_leftdest-_left;elseparent-_rightdest-_left;deletedest;}returntrue;}}returnfalse;}这段代码有几个需要注意的点这种情况左子树或右子树为空都适用六. 二叉搜索树key和key/value使用场景6.1 key搜索场景只有key作为关键码结构中只需要存储key即可关键码即为需要搜索到的值搜索场景只需要判断key在不在。key的搜索场景实现的⼆叉树搜索树支持增删查但是不支持修改修改key破坏搜索树结构了。场景1小区无人值守车库小区车库买了车位的业主的车才能进小区那么物业会把买了车位的业主的车牌号录入后台系统车辆进⼊时扫描车牌在不在系统中在则抬杆不在则提示非本小区车辆无法进入。场景2检查⼀篇英文文章单词拼写是否正确将词库中所有单词放入二叉搜索树读取文章中的单词查找是否在二叉搜索树中不在则波浪线标红提示。6.2 key/value搜索场景每⼀个关键码key都有与之对应的值valuevalue可以任意类型对象。树的结构中(结点)除了需要存储key还要存储对应的value增/删/查还是以key为关键字按二叉搜索树的规则进行比较可以快速查找到key对应的value。key/value的搜索场景实现的二叉树搜索树支持修改但是不支持修改key修改key破坏搜索树结构了可以修改value。场景1简单中英互译字典树的结构中(结点)存储key(英文)和vlaue(中文)搜索时输入英文则同时查找到了英文对应的中文。场景2商场无人值守车库入口进场时扫描车牌记录车牌和入场时间出口离场时扫描车牌查找入场时间用当前时间-入场时间计算出停车时长计算出停车费用缴费后抬杆车辆离场。场景3统计一篇文章中单词出现的次数读取一个单词查找单词是否存在不存在这个说明第一次出现单词1单词存在则单词对应的次数。key/value⼆叉搜索树代码实现namespaceKey_Value{templateclassK,classVstructBTNode{K _key;V _val;BTNodeK,V*_left;BTNodeK,V*_right;BTNode(constKkey,constVval):_key(key),_val(val),_left(nullptr),_right(nullptr){}};templateclassK,classVclassBSTree{typedefBTNodeK,VNode;public:BSTree()default;~BSTree(){Destroy(_root);}BSTree(constBSTreet){_rootCopy(t._root);}BSTreeoperator(BSTree tmp){std::swap(_root,tmp._root);return*this;}boolInsert(constKkey,constVval){if(_rootnullptr){_rootnewNode(key,val);returntrue;}Node*parentnullptr;// 父亲节点Node*cur_root;// 当前节点while(cur){if(cur-_keykey)// 当前节点的值大于key往左走{parentcur;curcur-_left;}elseif(cur-_keykey)// 当前节点的值小于key往右走{parentcur;curcur-_right;}else{returnfalse;}}// 找到空位置curnewNode(key,val);if(parent-_keykey){parent-_leftcur;}else{parent-_rightcur;}returntrue;}Node*Find(constKkey){Node*cur_root;while(cur){if(cur-_keykey)// 往左走{curcur-_left;}elseif(cur-_keykey){curcur-_right;}else{returncur;}}returnnullptr;}boolErase(constKkey){Node*cur_root;Node*parentnullptr;// 找key节点while(cur){if(cur-_keykey){parentcur;curcur-_right;}elseif(cur-_keykey){parentcur;curcur-_left;}else// 找到key了{// 左为空父亲节点连右子树if(cur-_leftnullptr){if(cur_root){_rootcur-_right;}else{//当前节点是父亲节点的左子树if(curparent-_left){parent-_leftcur-_right;}else//当前节点是父亲节点的右子树{parent-_rightcur-_right;}}deletecur;}// 右子树为空父亲节点连左子树elseif(cur-_rightnullptr){if(cur_root){_rootcur-_left;}else{//当前节点是父亲节点的左子树if(curparent-_left){parent-_leftcur-_left;}else{parent-_rightcur-_left;}}deletecur;}else//左右子树都不为空找左子树中最大的(最右节点){Node*destcur-_left;Node*parentcur;while(dest-_right){parentdest;destdest-_right;}cur-_keydest-_key;if(parent-_leftdest)// 删根节点parent-_leftdest-_left;elseparent-_rightdest-_left;deletedest;}returntrue;}}returnfalse;}void_InOrder(){InOrder(_root);coutendl;}private:voidInOrder(Node*_root){if(_rootnullptr)return;InOrder(_root-_left);cout_root-_key:_root-_valendl;InOrder(_root-_right);}voidDestroy(Node*_root){if(_rootnullptr)return;Destroy(_root-_left);Destroy(_root-_right);delete_root;}Node*Copy(Node*root){if(rootnullptr)returnnullptr;Node*newRootnewNode(root-_key,root-_val);newRoot-_leftCopy(root-_left);newRoot-_rightCopy(root-_right);returnnewRoot;}Node*_rootnullptr;};}七. 析构函数、拷贝构造以及赋值重载的实现析构函数将二叉树的每一个节点的内存空间都释放掉之前我们C语言的二叉树的销毁用的后序遍历由于我们在类外无法访问到_root指针我们可以将二叉树的销毁实现成私有成员函数然后在类内让析构函数去调用~BSTree(){Destroy(_root);}private:voidDestroy(Node*_root){if(_rootnullptr)return;Destroy(_root-_left);Destroy(_root-_right);delete_root;}拷贝构造拷贝构造的实现我们选择使用递归的方法先将根节点构造出来然后构建左子树再构建右子树。BSTree(constBSTreet){_rootCopy(t._root);}private:Node*Copy(Node*root){if(rootnullptr)returnnullptr;Node*newRootnewNode(root-_key,root-_val);newRoot-_leftCopy(root-_left);newRoot-_rightCopy(root-_right);returnnewRoot;}用下面的例子部分演示一下构造子树的过程赋值重载有了拷贝构造之后写现代写法的复制重载就很容易了只需要交换两个树的根节点的指针即可。BSTreeoperator(BSTree tmp){std::swap(_root,tmp._root);return*this;}结语以上我们就讲完了二叉搜索树的内容希望对大家有所帮助感谢大家的阅读欢迎大家批评指正

相关文章:

【C++笔记】二叉搜索树

前言 各位读者朋友们大家好!上期我们讲完了面向对象编程三大属性之一的多态,这一期我们再次开始数据结构二叉搜索树的讲解。 目录前言一. 二叉搜索树的概念二. 二叉搜索树的性能分析三. 二叉搜索树的插入四. 二叉搜索树的查找五. 二叉搜索树的删除六. 二…...

PyCaret特征工程:轻松构建专业级特征缩放与选择Pipeline

PyCaret特征工程:轻松构建专业级特征缩放与选择Pipeline 【免费下载链接】pycaret An open-source, low-code machine learning library in Python 项目地址: https://gitcode.com/gh_mirrors/py/pycaret PyCaret是一款开源的低代码机器学习库,它…...

Win-Debloat-Tools计划任务管理:优化系统后台运行的终极指南

Win-Debloat-Tools计划任务管理:优化系统后台运行的终极指南 【免费下载链接】Win-Debloat-Tools Re-imagining Windows like a minimal OS install, already debloated with minimal impact for most functionality. 项目地址: https://gitcode.com/gh_mirrors/w…...

Vue项目改造指南:轻松修改启动后的Logo和名称

目录 前言 一、修改前的准备工作 1.1 了解项目结构 1.2 准备素材 二、修改浏览器标签页图标和标题 2.1 替换Favicon图标 2.2 修改网页标题 2.3 验证修改效果 总结 前言 在Vue项目开发中,我们经常需要根据项目需求修改默认的品牌标识。无论是企业级管理系统…...

Django-Oscar优惠券与促销系统:10种营销策略的终极实现指南

Django-Oscar优惠券与促销系统:10种营销策略的终极实现指南 【免费下载链接】django-oscar django-oscar/django-oscar: 是一个基于 Django 的电子商务框架,可以用于快速开发和部署电子商务网站,提供了多种电子商务功能和插件扩展。 项目地…...

基于代价的连接条件下推,金仓数据库让我们不在焦虑

你是否遇到过这样的场景:一个看似复杂的SQL,在测试环境运行飞快,一到生产环境就"卡死",一查执行计划,发现子查询生成了一个巨大的中间结果集,导致后续操作全部陷入性能泥潭? 如果你正…...

复杂查询中 JOIN 条件下推失败导致的性能瓶颈-金仓数据库

文章目录前言一、问题背景1.1 客户场景中的典型痛点1.2 业界普遍面临的两大难点1.2.1 语义安全性(Equivalence)1.2.2 代价评估(Cost)二、传统方案的局限2.1 完整执行子查询2.2 生成庞大的中间结果集2.3 再与外层表进行 JOIN三、金…...

WHAT - 缓存命中 Cache Hit 和缓存未命中 Cache Miss

文章目录一、什么是缓存命中二、前端开发要知道哪些缓存机制(以及命中条件)1. 浏览器缓存(主要针对静态资源)常见的缓存位置关键 HTTP 头字段(决定命中与否)2. 前端应用层缓存(例如数据请求&…...

一文搞定常见网络安全技术:网络攻击与核心防范手段全景解析(建议收藏)

伴随着互联网的发展,它已经成为我们生活中不可或缺的存在,无论是个人还是企业,都离不开互联网。正因为互联网得到了重视,网络安全问题也随之加剧,给我们的信息安全造成严重威胁,而想要有效规避这些风险&…...

Linux网络安全从入门到精通:基础命令、安全配置与实战案例(保姆级教程)

Linux网络安全一直是IT行业中备受关注的话题,而红帽作为Linux操作系统的知名发行版,在网络安全领域也扮演着重要的角色。红帽公司一直致力于为用户提供安全可靠的Linux解决方案,以帮助用户建立强大的网络安全防护体系。 首先,红帽…...

cobbler + pxe+dhcp+tftp+httpd+kickstart无人值守装系统

一、cobbler简介 cobbler是基于Python2开发并整合pxe+kickstart技术的二次封装工具,简化了安装部署流程,增加了对多发行版的支持,并且有独立的web管理页面,极大方便了运维初级人员的学习和使用。另外cobbler还提供了API,方便二次开发。 该文章主要介绍使用cobbler自动装机…...

网络安全岗位全解析:从入门到优秀工程师的进阶路线图(建议收藏)

网络安全是什么? 首先说一下什么是网络安全? 网络安全工程师工作内容具体有哪些? 网络安全是确保网络系统的硬件、软件及其系统中的数据受到保护,不因偶然的或者恶意的原因而受到破坏、更改、泄露,系统连续可靠正常地…...

如何快速掌握Embark框架:从代码规范到贡献流程的完整指南

如何快速掌握Embark框架:从代码规范到贡献流程的完整指南 【免费下载链接】embark 项目地址: https://gitcode.com/gh_mirrors/emb/embark Embark是一个功能强大的区块链开发框架,它简化了以太坊DApp的开发流程,提供了从智能合约编译…...

RLHF在多模态领域的应用:MM-RLHF框架与视觉语言模型对齐技术

RLHF在多模态领域的应用:MM-RLHF框架与视觉语言模型对齐技术 【免费下载链接】awesome-RLHF A curated list of reinforcement learning with human feedback resources (continually updated) 项目地址: https://gitcode.com/gh_mirrors/aw/awesome-RLHF 多…...

从入门到精通:LedisDB命令完全指南,解锁高性能数据存储操作

从入门到精通:LedisDB命令完全指南,解锁高性能数据存储操作 【免费下载链接】ledisdb 项目地址: https://gitcode.com/gh_mirrors/led/ledisdb LedisDB是一款兼容Redis协议的高性能数据存储系统,支持多种数据结构和高级功能。本文将全…...

ExLlamaV2动态批处理生成器深度解析

ExLlamaV2动态批处理生成器深度解析 【免费下载链接】exllamav2 A fast inference library for running LLMs locally on modern consumer-class GPUs 项目地址: https://gitcode.com/gh_mirrors/ex/exllamav2 引言:大模型推理的性能瓶颈与解决方案 在大语言…...

每日八股文6.12

每日八股-6.12计算机网络1.当我们在浏览器中输入一个 URL 并按下回车后,到页面最终显示出来,这中间都发生了哪些关键步骤?2.请简述一下 JWT(JSON Web Tokens)的原理和校验机制3.DNS 是如何进行域名解析的?它…...

SecretVault强网杯2025 Web题解:从JWT绕过到HTTP头注入的实战剖析

1. 初探SecretVault:一个看似简单的Web应用 最近在复盘强网杯2025的一道Web题目,叫SecretVault。这道题挺有意思的,它表面上是一个密码保险箱应用,你可以登录、注册,然后把你的各种账号密码加密存进去。题目环境一打开…...

用UE5 Multi-User Editing实现远程团队协作:公网部署+会话管理全流程解析

用UE5 Multi-User Editing实现远程团队协作:公网部署会话管理全流程解析 最近和几个分布在不同城市的朋友一起捣鼓一个UE5的独立项目,最大的痛点就是资产和场景的同步。今天传个地图,明天发个蓝图,版本很快就乱成一锅粥。直到我们…...

Fabric、FISCO BCOS与以太坊:三大区块链平台的技术架构与应用场景解析

1. 开篇:为什么需要了解不同的区块链平台? 如果你刚开始接触区块链,可能会觉得眼花缭乱。以太坊、Fabric、FISCO BCOS……这些名字听起来都很厉害,但它们到底有什么区别?我该用哪个?这就像你要盖房子&#…...

幻兽帕鲁服务器搭建全攻略:从SteamCMD到端口转发一步到位

幻兽帕鲁私服搭建实战:从零构建稳定可联机的专属世界 最近身边不少朋友都沉迷于《幻兽帕鲁》这款游戏,但官服有时难免会遇到延迟、排队或者想和固定小圈子朋友一起玩的限制。于是,自己动手搭建一个专属服务器的念头就冒了出来。这听起来像是资…...

Charles实战:手把手教你模拟复杂网络环境下的弱网测试

1. 为什么你的App一到地铁里就卡?聊聊弱网测试那点事 不知道你有没有遇到过这种情况:早上通勤,在地铁里刷着新闻App,图片半天加载不出来,刷个短视频一直转圈圈,甚至点个外卖提交订单时直接卡死闪退。你可能…...

从柳树皮到实验室:水杨酸合成技术演进与化妆品原料安全标准解析

从柳树皮到实验室:水杨酸合成技术演进与化妆品原料安全标准解析 当我们谈论护肤品中的“刷酸”时,水杨酸几乎是一个绕不开的名字。它被成分党们奉为对抗黑头、闭口和痘痘的利器,但很少有人去深究,涂抹在脸上的那一滴精华或乳霜里&…...

[QCM6125][Android13] 关闭dm-verity后OTA升级兼容性校验的应对策略

1. 从一次失败的OTA升级说起:关闭dm-verity后的连锁反应 最近在折腾一块基于高通QCM6125平台的开发板,系统是Android 13。为了让设备获得更高的灵活性,比如能直接remount /分区进行一些调试和修改,我按照老习惯把dm-verity给关掉了…...

差分进化算法:从理论到实战的全局优化利器

1. 为什么说差分进化是你的下一个“秘密武器”? 大家好,我是老张,在AI和算法优化这个行当里摸爬滚打了十几年。今天想跟你聊聊一个我特别钟爱,并且在实际项目中屡建奇功的算法——差分进化。你可能听说过遗传算法、粒子群优化&…...

GIS开发必知:EPSG 4326和3857坐标系到底怎么选?附OpenLayers实战代码

GIS开发坐标系抉择:从原理到实战,深度解析4326与3857 最近在帮团队重构一个老旧的WebGIS项目时,我又一次被坐标系问题绊住了。数据源是标准的WGS84经纬度,但前端地图库默认渲染的却是Web墨卡托投影。页面上的几何图形拉伸变形&…...

基于eNSP的IPv4/IPv6双栈网络高可用与安全融合设计【企业园区网实战】

1. 项目背景与设计目标:为什么需要双栈高可用园区网? 大家好,我是老陈,一个在园区网里摸爬滚打了十多年的老网工。这些年,我亲眼看着网络从纯IPv4,到各种过渡技术,再到如今IPv6的全面铺开。很多…...

麒麟勒索软件攻击朝日集团事件解析:如何保护企业免受RaaS平台威胁

麒麟勒索软件攻击朝日集团事件解析:如何保护企业免受RaaS平台威胁 最近,一家全球知名的制造业巨头遭遇的网络攻击事件,在安全圈内外都引发了不小的震动。生产线停摆、供应链中断、敏感数据泄露,这些看似只存在于新闻中的场景&…...

智能工厂四大系统协同实战:ERP/PLM/MES/WMS数据流与接口设计全解析

1. 从“各自为政”到“协同作战”:为什么你的系统总在“打架”? 我干了这么多年智能工厂的规划和落地,发现一个特别普遍的现象:很多老板花大价钱上了ERP、PLM、MES、WMS,结果呢?数据还是对不上,…...

MTK SensorHub:从驱动注册到数据上报的完整流程剖析

1. 初识MTK SensorHub:手机里的“传感器大管家” 大家好,我是老张,在手机芯片和传感器这块摸爬滚打了十几年。今天咱们不聊那些虚头巴脑的概念,就掰开揉碎了讲讲MTK平台上一个非常核心但又有点神秘的东西——SensorHub。你可以把它…...