【C++高阶】高效搜索的秘密:深入解析搜索二叉树
📝个人主页🌹:Eternity._
⏩收录专栏⏪:C++ “ 登神长阶 ”
🤡往期回顾🤡:C++多态
🌹🌹期待您的关注 🌹🌹



❀二叉搜索树
- 📒1. 二叉搜索树
- 🎩二叉搜索树概念
- 🎈二叉搜索树操作
- 📕2. 二叉搜索树模拟实现
- 🧩二叉搜索树结构
- 🧩二叉搜索树操作
- 🌈插入
- 🌞遍历
- 🌙查找
- ⭐删除
- 🧩二叉搜索树默认成员函数
- 📜3. 二叉搜索树模拟实现(递归)
- 🌞插入
- 🌙查找
- ⭐删除
- 📚4. 二叉搜索树的应用
- 🍁KV模型
- 🍂KV模型实现
- 💧英汉词典
- 🔥计数
- 🌄二叉树巩固知识
- 📖5. 总结
前言: 在数据结构和算法的广阔领域中,二叉搜索树(Binary Search Tree,简称BST)无疑是一颗璀璨的明星。它以其高效的数据检索能力和独特的树形结构,在计算机科学领域扮演着举足轻重的角色。对于任何对编程和数据结构感兴趣的人来说,掌握二叉搜索树都是至关重要的一步
二叉搜索树不仅仅是一个简单的数据结构,它更是一种解决问题的方式和思维的体现。通过维护二叉树中每个节点的左子树所有值均小于它的值,右子树所有值均大于它的值的特性,二叉搜索树在插入、查找和删除操作中展现出了卓越的性能。这种特性使得二叉搜索树在各种应用中成为了一种理想的数据结构选择,从基础的算法练习到复杂的系统优化,都能见到它的身影
学习二叉搜索树并非易事。它需要我们深入理解其性质、原理和算法实现。我们需要掌握如何构建一棵二叉搜索树,如何遍历它,以及如何在其中进行高效的查找、插入和删除操作。这些都需要我们付出大量的时间和精力去学习和实践。我们将从二叉搜索树的基本概念出发,逐步深入到其性质、构建、遍历以及操作的实现
让我们一起踏上学习二叉搜索树的旅程,探索它带来的无尽可能!
(本文重在二叉搜索树的模拟实现与理解)
📒1. 二叉搜索树
🎩二叉搜索树概念
二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:
- 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
- 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
- 它的左右子树也分别为二叉搜索树

🎈二叉搜索树操作
首先,在二叉搜索树的操作中只支持插入,查找,删除,遍历,并不支持修改操作,因为在修改后谁也不能保证它依然是一棵二叉搜索树,二叉搜索树的时间复杂度范围在(O(logN)~O(N))
在二叉搜索树的遍历中一般采用中序遍历: 先遍历左子树,然后访问根节点,最后遍历右子树。在BST中,中序遍历会按照升序访问所有节点
二叉搜索树示例

int a[] = {8, 3, 1, 10, 6, 4, 7, 14, 13};
📕2. 二叉搜索树模拟实现
🧩二叉搜索树结构

二叉搜索树结构的和树形结构差不多,这意味着每个元素(通常称为节点)都有两个指针:一个指向前一个左子树,另一个指向右子树,因此我们需要单独再定义一个类来表示节点结构,每个节点再串联起来构成BST
(在模拟实现二叉搜索树时,不用定义命名空间,因为不会和库中发生冲突)
节点定义(示例):
template<class K>
struct BSTreeNode
{BSTreeNode<K>* _left;BSTreeNode<K>* _right;K _key;BSTreeNode(const K& key = K()):_key(key), _left(nullptr), _right(nullptr){}
};
BST定义(示例):
template<class K>
class BSTree
{typedef BSTreeNode<K> Node;
public:// 构造函数等可能的其他成员函数...
private:Node* _root = nullptr;
};
🧩二叉搜索树操作
🌈插入
插入的具体过程如下:
- 树为空,则直接新增节点,赋值给root指针
- 树不空,按二叉搜索树性质查找插入位置,插入新节点

插入代码实现(示例):
bool Insert(const K& key)
{// 当根为空时,直接插入if (_root == nullptr){_root = new Node(key);return true;}// 定义parent是因为,在最后找到插入位置时,需要parent将节点进行连接Node* parent = nullptr; Node* cur = _root;while (cur){parent = cur;// 插入的值比cur位置大时,cur往右移动if (key > cur->_key){cur = cur->_right;}// 插入的值比cur位置小时,cur往左移动else if (key < cur->_key){cur = cur->_left;}// 当插入的值和cur位置相等时,直接退出,因为二叉搜索树不允许有相同的元素else{return false;}}// 将插入位置的新节点与二叉搜索树连接cur = new Node(key);if (parent->_key < key){parent->_right = cur;}else{parent->_left = cur;}return true;
}
🌞遍历
在二叉搜索树的遍历上,我们依旧采用当初二叉树时的中序遍历,但是我们想要递归遍历就必须调用节点,这里我们要调用两层
遍历代码实现(示例):
void InOrder()
{_InOrder(_root);cout << endl;
}void _InOrder(Node* root)
{// 递归出口if (root == nullptr){return;}_InOrder(root->_left);cout << root->_key << " " ;_InOrder(root->_right);
}
遍历比较简单奥,我们接着往下讲
🌙查找
二叉搜索树的查找
- 从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找
- 最多查找高度次,走到到空,还没找到,这个值不存在
查找代码实现(示例):
bool Find(const K& key)
{Node* cur = _root;while (cur){// 查找的值比cur大,cur往右移动if (key > cur->_key){cur = cur->_right;}// 查找的值比cur小,cur往左移动else if (key < cur->_key){cur = cur->_left;}// 找到key,返回trueelse{return true;}}return false; // 找不到key,返回false
}
⭐删除
首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面情况:
- 要删除的结点无孩子结点
- 要删除的结点只有左孩子结点
- 要删除的结点只有右孩子结点
- 要删除的结点有左、右孩子结点
而上面四种情况可以转化成下面的情况:
- 删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点–直接删除
- 删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点–直接删除
- 在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点中,再来处理该结点的删除问题–替换法删除

这三种情况就是我们模拟实现删除的方法!
删除代码实现(示例):
bool Erase(const K& key)
{Node* cur = _root;Node* parent = nullptr;while (cur){parent = cur;if (key > cur->_key){cur = cur->_right;}else if (key < cur->_key){cur = cur->_left;}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;}}}// 右为空else if (cur->_right == nullptr){// 当需要删除的是头节点时if (cur == _root){_root = cur->_left;}else{if (cur == parent->_left){parent->_left = cur->_left;}else{parent->_right = cur->_left;}}}// 两边都不为空else{// 这里与外层不是同一块作用域,所以可以再次定义parent,不把parent定义为nullptr是因为//,可能不进入下面循环导致对parent空指针的使用Node* subLeft = cur->_right; // 定义右数节点Node* parent = cur;while (subLeft->_left){parent = subLeft;subLeft = subLeft->_left;}swap(cur->_key, subLeft->_key); // 替换右子树的最左节点if (subLeft == parent->_left){// 最左节点肯定没有左孩子,所以让parent接管它的右子树parent->_left = subLeft->_right;}else{parent->_right = subLeft->_right;}delete subLeft;}return true;}}return false;
}
🧩二叉搜索树默认成员函数
构造
BSTree() = default; // 显式地定义默认构造函数
拷贝构造
BSTree(const BSTree<K>& t)
{_root = Copy(t._root);
}Node* Copy(Node* root)
{if (root == nullptr){return nullptr;}// 递归进行拷贝构造Node* newroot = new Node(root->_key);newroot->_left = Copy(root->_left);newroot->_right = Copy(root->_right);return newroot;
}
赋值重载
BSTree<K>& operator=(BSTree<K> t)
{// 现代写法-> 直接调用swapswap(_root, t._root);return *this;
}
析构
~BSTree()
{Destory(_root);
}void Destory(Node*& _root)
{if (_root == nullptr){return;}// 递归调用析构Destory(_root->_left);Destory(_root->_right);delete _root;_root == nullptr;
}
📜3. 二叉搜索树模拟实现(递归)
在进行递归操作的模拟实现时,一般都要传节点,进行多层的调用,因为我们都要定义两层
bool FindR(const K& key)
{return _FindR(_root, key);
}bool InsertR(const K& key)
{return _InsertR(_root, key);
}bool EraseR(const K& key)
{return _EraseR(_root, key);
}
🌞插入
代码实现(示例):
bool _InsertR(Node*& _root, const K& key)
{// 递归出口if (_root == nullptr){// 这里我们无需在进行对新节点的连接,因为我们是传引用传参,_root = new Node(key);return true;}if (key > _root->_key){return _InsertR(_root->_right, key);}else if (key < _root->_key){return _InsertR(_root->_left, key);}else{return false;}
}
🌙查找
代码实现(示例):
bool _FindR(Node* _root, const K& key){if (_root == nullptr){return false;}if (key > _root->_key){return _FindR(_root->_right, key);}else if (key < _root->_key){return _FindR(_root->_left, key);}else{return true;}}
⭐删除
代码实现(示例):
bool _EraseR(Node*& _root, const K& key)
{if (_root == nullptr){return false;}if (key > _root->_key){return _EraseR(_root->_right, key);}else if (key < _root->_key){return _EraseR(_root->_left, 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 _EraseR(_root->_right, key);}}
}
📚4. 二叉搜索树的应用
🍁KV模型
KV模型:每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对。该种方式在现实生活中非常常见:
- 比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英
文单词与其对应的中文<word, chinese>就构成一种键值对- 再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出
现次数就是<word, count>就构成一种键值对
namespace kv // 避免与之前k模型冲突
{template<class K, class V>struct BSTreeNode{BSTreeNode<K>* _left;BSTreeNode<K>* _right;K _key;V _value;BSTreeNode(const K& key = K(), const V& value = V()): _left(nullptr), _right(nullptr), _key(key), _value(value){}};template<class K, class V>class BSTree{typedef BSTreeNode<K, V> Node;public:// 构造函数等可能的其他成员函数... // 在成员函数中,我们只需要在insert中加入value元素即可private:Node* _root = nullptr;};
}
在成员函数中,我们只需要在Insert中加入value元素即可
🍂KV模型实现
💧英汉词典
代码实现(示例):
int main()
{kv::BSTree<string, string> dict;dict.insert("left", "左边、剩余");dict.insert("string", "字符串");dict.insert("hahaha", "哈哈");dict.insert("Eternity", "永恒");dict.insert("sort", "排序");dict.InOrder();string str;while (cin >> str){kv::BSTreeNode<string, string>* ret = dict.Find(str);if (ret){cout << ret->_value << endl;}else{cout << "无此单词" << endl;}}
}
🔥计数
代码实现(示例):
int main()
{string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜",
"苹果", "香蕉", "苹果", "香蕉" };kv::BSTree<string, int> countTree;for (auto& e : arr){kv::BSTreeNode<string, int>* ret = countTree.Find(e);if (ret == nullptr){countTree.insert(e, 1);}else{ret->_value++;}}countTree.InOrder();return 0;
}
🌄二叉树巩固知识
最后在这给大家推荐几道巩固二叉树的编程题
二叉树创建字符串
二叉树的分层遍历
找到树中两个指定节点的最近公共祖先
二叉树搜索树转换成排序双向链表
根据一棵树的前序遍历与中序遍历构造二叉树
二叉树中序遍历 ,非递归迭代实现
📖5. 总结
经过我们一同对搜索二叉树的深入学习和探索,相信你已经对这种数据结构有了全面而深刻的理解。搜索二叉树以其独特的性质在数据检索领域展现了出色的性能,无论是插入、删除还是查找操作,都体现了其高效和灵活的特点
学习的道路永无止境。虽然我们已经走过了搜索二叉树的基本概念和操作的学习之旅,但这只是你编程旅程中的一个站点。前方还有更多数据结构等待你去探索,更多算法等待你去实践
不要忘记持续学习和自我提升。计算机科学是一个日新月异的领域,新的技术和算法不断涌现。只有保持对知识的渴望和追求,我们才能在这个领域中不断前行,让我们一起在学习的道路上不断前行!

希望本文能够为你提供有益的参考和启示,让我们一起在编程的道路上不断前行!
谢谢大家支持本篇到这里就结束了,祝大家天天开心!

相关文章:
【C++高阶】高效搜索的秘密:深入解析搜索二叉树
📝个人主页🌹:Eternity._ ⏩收录专栏⏪:C “ 登神长阶 ” 🤡往期回顾🤡:C多态 🌹🌹期待您的关注 🌹🌹 ❀二叉搜索树 📒1. 二叉搜索树&…...
《软件定义安全》之七:SDN安全案例
第7章 SDN安全案例 1.DDoS缓解 1.1 Radware DefenseFlow/Defense4All Radware在开源的SDN控制器平台OpenDaylight(ODL)上集成了一套抗DDoS的模块和应用,称为Defense4ALL。其架构如下图,主要有两部分:控制器中的安全…...
java语言his系统医保接口 云HIS系统首页功能实现springboot框架+Saas模式 his系统项目源码
java语言his系统医保接口 云HIS系统首页功能实现springboot框架Saas模式 his系统项目源码 HIS系统的实施旨在整个医院建设企业级的计算机网络系统,并在其基础上构建企业级的应用系统,实现整个医院的人、财、物等各种信息的顺畅流通和高度共享,…...
使用vscode插件du-i18n处理前端项目国际化翻译多语言
前段时间我写了一篇关于项目国际化使用I18n组件的文章,Vue3 TS 使用国际化组件I18n,那个时候还没真正在项目中使用,需求排期还没有定,相当于是预研。 当时就看了一下大概怎么用,改了一个简单的页面,最近需…...
双系统下,如何隐藏另一个系统分区?
前言 最近有小伙伴在公众号下留言: 小伙伴说:“双系统时,非当前系统的系统盘能不能屏蔽?!比如Win7的系统盘在Win10系统时,盘符成了D盘,安装应用软件时,有些文件就到了D盘࿰…...
电脑意外出现user32.dll丢失的八种修复方法,有效解决user32.dll文件丢失
遇到与 user32.dll 相关的错误通常是因为该文件已损坏、丢失、或者与某些软件冲突。今天这篇文章寄给大家介绍八种修复user32.dll丢失的方法,下面是一步步的详细教程来解决这个问题。 1. 重新启动电脑 第一步总是最简单的:重新启动你的电脑。许多小问题…...
CUDA系列-Kernel Launch-8
这里写目录标题 kernel launch 本章主要追踪一下kernel launch的流程,会不断完善。 kernel launch 先抛出一个问题,如果在一个循环中不断的发送kernel(kernel 内部while死循环),会是什么结果。 // kernel 函数 __glo…...
# 消息中间件 RocketMQ 高级功能和源码分析(四)
消息中间件 RocketMQ 高级功能和源码分析(四) 一、 消息中间件 RocketMQ 源码分析:回顾 NameServer 架构设计。 1、RocketMQ 架构设计 消息中间件的设计思路一般是基于主题订阅发布的机制,消息生产者(Producer&…...
如何通过数据库与AI实现以图搜图?OceanBase向量功能详解
OceanBase支持向量数据库的基础能力 当前,数据库存储系统与人工智能技术的结合,可以体现在两个主要的应用方向上。 一、近似搜索。它利用大语言模型(LLM,简称大模型)的嵌入(embedding)技术&am…...
Kafka内外网分流配置listeners和advertised.listeners
问题背景: Kafka部署在内网,内网Java服务会使用Kafka收发消息,另外,Java服务会与其他第三方系统使用kafka实现数据同步,也就是外网也会发送消息到kafka,外网IP做了端口映射到了内网,advertised…...
Linux系统编程——网络编程
目录 一、对于Socket、TCP/UDP、端口号的认知: 1.1 什么是Socket: 1.2 TCP/UDP对比: 1.3 端口号的作用: 二、字节序 2.1 字节序相关概念: 2.2 为什么会有字节序: 2.3 主机字节序转换成网络字节序函数…...
信息安全技术基础知识-经典题目
【第1题】 1.在信息安全领域,基本的安全性原则包括机密性(Confidentiality)、完整性(Integrity)和 可用性(Availability)。机密性指保护信息在使用、传输和存储时 (1) 。信息加密是保证系统机密性的常用手段。使用哈希校验是保证数据完整性的常用方法。可用性指保证…...
nextjs(持续学习中)
return ( <p className{${lusitana.className} text-xl text-gray-800 md:text-3xl md:leading-normal}> Welcome to Acme. This is the example for the{’ } Next.js Learn Course , brought to you by Vercel. ); } 在顶级 /public 文件夹下提供静态资产 **默认 /…...
数据预处理与特征工程、过拟合与欠拟合
数据预处理与特征工程 常用的数据预处理步骤 向量化:将数据转换成pytorch张量值归一化:将特定特征的数据表示成均值为0,标准差为1的数据的过程;取较小的值:通常在0和1之间;相同值域处理缺失值特征工程&am…...
甲辰年五月十四风雨思
甲辰年五月十四风雨思 夜雨消暑气,远光归家心。 只待万窗明,朝夕千家勤。 苦乐言行得,酸甜日常品。 宫商角徵羽,仁义礼智信。...
java分别使用 iText 7 库和iText 5 库 将excel转成PDF导出,以及如何对excel转PDF合并单元格
第一种 package com.junfun.pms.report.util;import com.itextpdf.kernel.font.PdfFontFactory; import com.itextpdf.layout.Document; import com.itextpdf.layout.element.Paragraph; import com.itextpdf.layout.property.TextAlignment; import com.itextpdf.layout.prop…...
Java特性之设计模式【访问者模式】
一、访问者模式 概述 在访问者模式(Visitor Pattern)中,我们使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。这种类型的设计模式属于行为型模式。根据模式&…...
【教师资格证考试综合素质——法律专项】未成年人保护法笔记以及练习题
《中华人民共和国未成年人保护法》 目录 第一章 总 则 第二章 家庭保护 第三章 学校保护 第四章 社会保护 第五章 网络保护 第六章 政府保护 第七章 司法保护 第八章 法律责任 第九章 附 则 介一.首次颁布:第一部《中华人民共和国未成年人保护法…...
6.19作业
TCP服务器 #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <unistd.h> #include <arpa/inet.h> #include <netinet/in.h> #include <string.h>#define PORT 8888 #define IP "192.168.124.39&q…...
java 线程之间通信-volatile 和 synchronized
你好,我是 shengjk1,多年大厂经验,努力构建 通俗易懂的、好玩的编程语言教程。 欢迎关注!你会有如下收益: 了解大厂经验拥有和大厂相匹配的技术等 希望看什么,评论或者私信告诉我! 文章目录 一…...
QMC5883L的驱动
简介 本篇文章的代码已经上传到了github上面,开源代码 作为一个电子罗盘模块,我们可以通过I2C从中获取偏航角yaw,相对于六轴陀螺仪的yaw,qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...
vue3 字体颜色设置的多种方式
在Vue 3中设置字体颜色可以通过多种方式实现,这取决于你是想在组件内部直接设置,还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法: 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...
淘宝扭蛋机小程序系统开发:打造互动性强的购物平台
淘宝扭蛋机小程序系统的开发,旨在打造一个互动性强的购物平台,让用户在购物的同时,能够享受到更多的乐趣和惊喜。 淘宝扭蛋机小程序系统拥有丰富的互动功能。用户可以通过虚拟摇杆操作扭蛋机,实现旋转、抽拉等动作,增…...
Spring AI Chat Memory 实战指南:Local 与 JDBC 存储集成
一个面向 Java 开发者的 Sring-Ai 示例工程项目,该项目是一个 Spring AI 快速入门的样例工程项目,旨在通过一些小的案例展示 Spring AI 框架的核心功能和使用方法。 项目采用模块化设计,每个模块都专注于特定的功能领域,便于学习和…...
嵌入式常见 CPU 架构
架构类型架构厂商芯片厂商典型芯片特点与应用场景PICRISC (8/16 位)MicrochipMicrochipPIC16F877A、PIC18F4550简化指令集,单周期执行;低功耗、CIP 独立外设;用于家电、小电机控制、安防面板等嵌入式场景8051CISC (8 位)Intel(原始…...
uniapp 集成腾讯云 IM 富媒体消息(地理位置/文件)
UniApp 集成腾讯云 IM 富媒体消息全攻略(地理位置/文件) 一、功能实现原理 腾讯云 IM 通过 消息扩展机制 支持富媒体类型,核心实现方式: 标准消息类型:直接使用 SDK 内置类型(文件、图片等)自…...
关于疲劳分析的各种方法
疲劳寿命预测方法很多。按疲劳裂纹形成寿命预测的基本假定和控制参数,可分为名义应力法、局部应力一应变法、能量法、场强法等。 1名义应力法 名义应力法是以结构的名义应力为试验和寿命估算的基础,采用雨流法取出一个个相互独立、互不相关的应力循环&…...
鸿蒙APP测试实战:从HDC命令到专项测试
普通APP的测试与鸿蒙APP的测试有一些共同的特征,但是也有一些区别,其中共同特征是,它们都可以通过cmd的命令提示符工具来进行app的性能测试。 其中区别主要是,对于稳定性测试的命令的区别,性能指标获取方式的命令的区…...
达梦使用存储过程实现删除重复记录、判断并添加主键和自增列的逻辑
在达梦数据库中,要确保主键的唯一性约束,可以在存储过程的最前面添加删除重复记录的逻辑。以下是一个完整的存储过程,包含删除重复记录、判断并添加主键和自增列的逻辑: 存储过程示例 -- 切换到指定模式;schema_name 是目标模…...
