【C++进阶】二叉搜索树(来自二叉树的复仇)

🪐🪐🪐欢迎来到程序员餐厅💫💫💫

          主厨:邪王真眼

 主厨的主页:Chef‘s blog  

 所属专栏:c++大冒险
 

 总有光环在陨落,总有新星在闪烁


[本节目标]

1. 二叉搜索树的介绍

2. 二叉搜索树的实现

3.二叉树搜索树应用

一.二叉树的介绍

二叉搜索树(BST,Binary Search Tree)又称二叉排序树或二叉查找树。
它是一棵空树,或者是具有以下性质的二叉树:
  1. 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  2. 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  3. 它的左右子树也分别为二叉搜索树

2. 二叉搜索树的实现 

本次二叉搜索树一律是小数放左边,大数放右边。

2.1节点

结合二叉树的知识,我们只知道二叉搜索树是分别写了两个类,一个是节点的类,一个是整个二叉树的类

template<class T>
struct BSTNode
{BSTNode(const T& val = T()):_val (val),_left(nullptr),_right(nullptr){}BSTNode<T>* _left;BSTNode<T>* _right;T _val;
};

注意事项:

             无论何时调用类模板,都要把模板参数给出

2.2成员变量

template<class T>
class BSTree
{typedef BSTNode<T> Node;
private:Node* _node;
};

注意事项:

              为了方便等会调用节点模板,我们先把他typedef为Node

2.3构造函数

BSTree()
:_node(nullptr)
{}

简简单单的用nullptr初始化一波

2.4拷贝构造函数

BSTree(const BSTree<T>& b)
{_root = Copy(b._node);
}
Node* Copy(Node *b)
{if (b == nullptr)return nullptr;Node* p = new Node(b->_val);p->_left = Copy(b->_left);p->_right = Copy(b->_right);return p;
}

注意事项:

  1.  为了方便用递归,我们用别的函数实现拷贝,在用拷贝构造函数调用那个函数(捡便宜属于是)。
  2. Copy用的是前序遍历来实现的

2.5赋值操作符重载

BSTree<T>& operator=(BSTree<T>b)
{swap(b._node, _node);return *this;
}

注意事项:

              这次我们依旧用摩登的实现方法,传过去一份临时拷贝,在交换根节点即可。

2.6析构函数

~BSTree()
{destory(_node);
}
void destory(Node* node)
{if (node == nullptr)return;if (node->_left)destory(node->_left);if (node->_right)destory(node->_right);delete node;
}

注意事项:

  1. 同样的道理,我们也是又写了一个函数去实现销毁功能,再让析构函数调用,主要原因还是析构函数的接口不合适
  2. destory用的是后序遍历

2.7中序遍历二叉树

	void _InOrder(Node* node){if(node->_left)_InOrder(node->_left);cout << node->_val << endl;if(node->_right)_InOrder(node->_right);}void InOrder(){_InOrder(_node);}

注意事项:

               中序遍历可以得到二叉搜索树的升序或降序排列,如图

2.8查找

查找是二叉搜索树的一大优势,毕竟这log N的速度谁不爱呢?

2.8.1迭代实现

Node* Find(const T& val)
{Node* node = _node;while (node){if (node->_val == val)return node;else if (node->_val > val)node = node->_left;elsenode = node->_right;}return nullptr;
}

注意事项:

  1. 当前节点的val大于目标val是去左边找
  2. 当前节点的val小于目标val是去右边找
  3. 结果找到了就返回节点的指针,否则返回nullptr

2.8.2递归实现

bool RFind(const T& val)
{return _RFind(_node, val);
}
bool _RFind(Node*node,const T& val)
{if (node == nullptr)return false;if (val == node->_val)return true;return _RFind(node->_left, val) || _RFind(node->_left, val);}

注意事项:

               这个返回指针很困难,所以我们直接返回真假表示有无该节点

2.9插入

2.9.1迭代

bool Insert(const T& val)
{if (_node == nullptr){_node = new Node(val);}else{Node* parent = nullptr;Node* child = _node;Node* ptr = new Node(val);while (child){parent = child;if (_node->_val > val){child = child->_left;}else if (_node->_val < val){child = child->_right;}elsereturn false;}if (parent->_val > val)parent->_left = ptr;if (parent->_val < val)parent->_right = ptr;}return true;
}

注意事项:

  • 1.我们传的是引用,当发现根节点是空时可以直接修改根节点而不用搞二级指针
  • 2.我们定义父亲节点和孩子节点,在循环中找到要把值放到那个父亲节点的孩子里
  • 3.判断是放到父亲的左节点还是右节点。
  • 4.如果发现该值已经存在了,则返回false,否则插入成功,返回true

2.9.2递归

bool RInsert(const T& val)
{_RInsert(_node, val);
}
bool _RInsert(Node*& p, const T& val)
{if (p == nullptr){p = new Node(val);return true;}if (p->_val > val)return RInsert(p->_left, val);else if (p->_val < val)return RInsert(p->_right, val);elsereturn false;
}

注意事项:

  • 1.还是函数调用另一个函数(我们成为工具人)
  • 2.我们传的是引用,所以不需要父亲节点了,直接修改即可
  • 3.果发现该值已经存在了,则返回false,否则插入成功,返回true

2.10删除(重难点)

2.10.1迭代

bool Erase(const T& val)
{if (_node == nullptr)return true;Node* cur = _node;Node* parent_node = nullptr;while (cur){if (cur->_val == val)break;else if (cur->_val > val){parent_node = cur;cur = cur->_left;}else{parent_node = cur;cur = cur->_right;}}if (cur == nullptr)return false;if (cur->_left == nullptr){if (parent_node == nullptr)_node = _node->_right;else if (parent_node->_left == cur)parent_node->_left = cur->_right;else if (parent_node->_right == cur)parent_node->_right = cur->_right;delete cur;}else if (cur->_right == nullptr){if (parent_node == nullptr)_node = _node->_left;else if (parent_node->_left == cur)parent_node->_left = cur->_left;elseparent_node->_right = cur->_left;delete cur;}else{Node* leftmax = cur->_left;Node* parent_leftmax = cur;while (leftmax->_right){parent_leftmax = leftmax;leftmax = leftmax->_right;}cur->_val = leftmax->_val;if (parent_leftmax->_left)parent_leftmax->_left = leftmax->_left;elseparent_leftmax->_right = leftmax->_left;delete leftmax;}
}
首先查找元素是否在二叉搜索树中,如果不存在,则返回 , 否则要删除的结点可能分下面四种情
况:
  • a. 要删除的结点无孩子结点
  • b. 要删除的结点只有左孩子结点
  • c. 要删除的结点只有右孩子结点
  • d. 要删除的结点有左、右孩子结点
看起来有待删除节点有 4 中情况,实际情况 a 可以与情况 b 或者 c 合并起来,因此真正的删除过程
如下:
  • 情况b:删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点--直接删除
  • 情况c:删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点--直接删除
  • 情况d:在它的左子树中寻找最大的节点a,用它的值填补到被删除节点中,再来处理结点a--替换法删除

在此基础上,我们还要对每种情况下删除节点是否为根节点进行讨论

2.10.2递归

	bool _RErase(Node*&node,const T&val){if (node == nullptr)return false;if (node->_val > val)return _RErase(node->_left, val);if (node->_val < val)return _RErase(node->_right, val);else{if (node->_left == nullptr){Node* right = node->_right;delete node;node = right;}else if (node->_right == nullptr){Node* left = node->_left;delete node;node =left;}else{Node* p = node->_left;while (p->_right){p = p->_right;}swap(node->_val , p->_val);return  _RErase(node->_left,val);}return true;}}bool RErase(const T& val){return _RErase(_node,val);}

注意事项:

  • 1.由于使用了引用,对左子树为空或右子树为空不再需要父亲节点的帮助
  • 2.若左右子树都不为空,则交换letfmax和node数值后,通过递归消除leftmax
  • 3.最后递归传参不要直接传根节点,而是node->left,因为之前的交换打乱了二叉树的结构,只能确定node->_left这棵树还是结构正确的

三.二叉树搜索树应用

3.1K模型

K 模型即只有 key 作为关键码,结构中 只需要存储Key 即可,关键码即为需要搜索到 的值
比如: 给一个单词 word ,判断该单词是否拼写正确 ,具体方式如下:
  1. 以词库中所有单词集合中的每个单词作为key,构建一棵二叉搜索树
  2. 在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。

3.2. KV模型:

每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对 。该种方式在现实生活中非常常见:
  1. 比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英 文单词与其对应的中文<word, chinese>就构成一种键值对;
  2. 比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出 现次数就是<word, count>就构成一种键值对
// 改造二叉搜索树为KV结构
template<class K, class V>
struct BSTNode{BSTNode(const K& key = K(), const V& value = V()): _pLeft(nullptr) , _pRight(nullptr), _key(key), _Value(value){}BSTNode<T>* _pLeft;BSTNode<T>* _pRight;K _key;V _value};
template<class K, class V>
class BSTree{typedef BSTNode<K, V> Node;typedef Node* PNode;
public:BSTree(): _pRoot(nullptr){}PNode Find(const K& key);bool Insert(const K& key, const V& value)bool Erase(const K& key)
private:PNode _pRoot;};
void TestBSTree3()
{// 输入单词,查找单词对应的中文翻译BSTree<string, string> dict;dict.Insert("string", "字符串");dict.Insert("tree", "树");dict.Insert("left", "左边、剩余");dict.Insert("right", "右边");dict.Insert("sort", "排序");// 插入词库中所有单词string str;while (cin>>str){BSTreeNode<string, string>* ret = dict.Find(str);if (ret == nullptr){cout << "单词拼写错误,词库中没有这个单词:" <<str <<endl;}else{cout << str << "中文翻译:" << ret->_value << endl;}}
}
void TestBSTree4()
{// 统计水果出现的次数string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", 
"苹果", "香蕉", "苹果", "香蕉" };BSTree<string, int> countTree;for (const auto& str : arr){// 先查找水果在不在搜索树中// 1、不在,说明水果第一次出现,则插入<水果, 1>// 2、在,则查找到的节点中水果对应的次数++//BSTreeNode<string, int>* ret = countTree.Find(str);auto ret = countTree.Find(str);if (ret == NULL){countTree.Insert(str, 1);}else{ret->_value++;}}countTree.InOrder();
}

总结:

今天我们学习了二叉树里的扛把子——二叉搜索树,细致地模拟了他的接口的实现(递归与迭代),接着讲解了他的应用——K模型和KV模型,最后把KV写了一遍。

觉得有帮助就点赞关注支持一下吧

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

软件架构风格_2.调用/返回体系结构风格

调用/返回风格是指在系统中采用了调用与返回机制。利用调用-返回实际上是一种分而治之的策略&#xff0c;其主要思想是将一个复杂的大系统分解为若干子系统&#xff0c;以便降低复杂度&#xff0c;并且增加可修改性。程序从其执行起点开始执行该构件的代码&#xff0c;程序执行…

使用 Docker 部署 Puter 云桌面系统

1&#xff09;Puter 介绍 :::info GitHub&#xff1a;https://github.com/HeyPuter/puter ::: Puter 是一个先进的开源桌面环境&#xff0c;运行在浏览器中&#xff0c;旨在具备丰富的功能、异常快速和高度可扩展性。它可以用于构建远程桌面环境&#xff0c;也可以作为云存储服…

基于单片机PM2.5监测系统仿真设计

**单片机设计介绍&#xff0c;基于单片机PM2.5监测系统仿真设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机PM2.5监测系统仿真设计概要主要涉及通过单片机实现对PM2.5浓度的实时监测与显示&#xff0c;并通过仿真工…

Python快速入门系列-8(Python数据分析与可视化)

第八章:Python数据分析与可视化 8.1 数据处理与清洗8.1.1 数据加载与查看8.1.2 数据清洗与处理8.1.3 数据转换与整理8.2 数据可视化工具介绍8.2.1 Matplotlib8.2.2 Seaborn8.2.3 Plotly8.3 数据挖掘与机器学习简介8.3.1 Scikit-learn8.3.2 TensorFlow总结在本章中,我们将探讨…

hive词频统计---文件始终上传不来

目录 准备工作&#xff1a; 文件内容&#xff1a; 创建数据库及表 将文件上传到&#xff1a;上传到/user/hive/warehouse/db1.db/t_word目录下 hive里面查询&#xff0c;始终报错&#xff1a;&#xff08;直接查询也是不行&#xff09; 解决方案&#xff1a; 准备工作&am…

用于AGV物流机器人的爱普生陀螺仪传感器XV7000系列

适用于AGV物流机器人的爱普生陀螺仪传感器XV7000系列:XV7001BB&#xff0c;XV7011BB。以前我们都知道XV7001BB&#xff0c;XV7011BB适用于扫地机器人&#xff0c;其实对于AGV物流机器人来说&#xff0c;XV7000系列生陀螺仪传感器也是其中重要一环。AGV机器人又叫做AGV搬运机器人…

基于java+SpringBoot+Vue的校园交友网站设计与实现

基于javaSpringBootVue的校园交友网站设计与实现 开发语言: Java 数据库: MySQL技术: SpringBoot MyBatis工具: IDEA/Eclipse、Navicat、Maven 系统展示 前台展示 后台展示 系统简介 整体功能包含&#xff1a; 校园交友网站是一个为在校师生提供一个交流互动、寻找朋友的…

手搓 Docker Image Creator(DIC)工具(02):预备知识

此节主要简单介绍一下 Docker、Dockerfile 的基本概念&#xff0c;Dockerfile 对的基本语法&#xff0c;Windows 和 macOS 下 Docker 桌面的安装&#xff0c;Docker 镜像的创建和运行测试等。 1 关于 Docker Docker 是一个开源的应用容器引擎&#xff0c;它允许开发者打包应用…

Python实现BOA蝴蝶优化算法优化BP神经网络回归模型(BP神经网络回归算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 蝴蝶优化算法(butterfly optimization algorithm, BOA)是Arora 等人于2019年提出的一种元启发式智能算…

Incus:新一代容器与虚拟机编排管理引擎

Incus是什么&#xff1f; Incus是一个用于编排管理应用型容器、系统型容器及虚拟机实例的管理工具。它是对 Canonical LXD 的继承与发展&#xff0c;引入了更多的存储驱动支持。 Incus项目的产品地址&#xff1a;Linux Containers - Incus - Introduction 在 LXC-Incus 项目…

【蓝牙协议栈】【BLE】【ATT】低功耗蓝牙之属性协议介绍

1. 精讲蓝牙协议栈&#xff08;Bluetooth Stack&#xff09;&#xff1a;SPP/A2DP/AVRCP/HFP/PBAP/IAP2/HID/MAP/OPP/PAN/GATTC/GATTS/HOGP等协议理论 2. 欢迎大家关注和订阅&#xff0c;【蓝牙协议栈】和【Android Bluetooth Stack】专栏会持续更新中.....敬请期待&#xff01…

HarmonyOS 应用开发之通过数据管理服务实现数据共享静默访问

场景介绍 典型跨应用访问数据的用户场景下&#xff0c;数据提供方会存在多次被拉起的情况。 为了降低数据提供方拉起次数&#xff0c;提高访问速度&#xff0c;OpenHarmony提供了一种不拉起数据提供方直接访问数据库的方式&#xff0c;即静默数据访问。 静默数据访问通过数据…

uniApp使用XR-Frame创建3D场景(4)金属度和粗糙度

上一篇讲解了如何在uniApp中创建xr-frame子组件并创建简单的3D场景。 这一篇我们讲解xr-frame中关于mesh网格材质的金属度和粗糙度的设置。 1.先看源码 <xr-scene render-system"alpha:true" bind:ready"handleReady"> <xr-node visible"{…

9、鸿蒙学习-开发及引用静态共享包(API 9)

HAR&#xff08;Harmony Archive&#xff09;是静态共享包&#xff0c;可以包含代码、C库、资源和配置文件。通过HAR可以实现多个模块或多个工程共享ArkUI组件、资源等相关代码。HAR不同于HAP&#xff0c;不能独立安装运行在设备上&#xff0c;只能作为应用模块的依赖项被引用。…

SOC内部集成网络MAC外设+ PHY网络芯片方案:PHY芯片基础知识

一. 简介 本文简单了解一下 "SOC内部集成网络MAC外设 PHY网络芯片方案" 这个网络硬件方案中涉及的 PHY网络芯片的基础知识。 二. PHY芯片基础知识 PHY 是 IEEE 802.3 规定的一个标准模块。 1. IEEE规定了PHY芯片的前 16个寄存器功能是一样的 前面说了&#xf…

GRU实现时间序列预测(PyTorch版)

&#x1f4a5;项目专栏&#xff1a;【深度学习时间序列预测案例】零基础入门经典深度学习时间序列预测项目实战&#xff08;附代码数据集原理介绍&#xff09; 文章目录 前言一、基于PyTorch搭建GRU模型实现风速时间序列预测二、时序数据集的制作三、数据归一化四、数据集加载器…

查看图片某点亮度

一背景 光强度的评价通常涉及对光源发出的光的量进行测量和描述。这种评价可以通过多种方式进行&#xff0c;但最常见的是使用光强单位“坎德拉”&#xff08;candela&#xff0c;cd&#xff09;来表示。坎德拉是国际单位制&#xff08;SI&#xff09;中光强度的单位&#xff…

WSL安装与使用

开启之后&#xff0c;会提示你重启电脑才能使配置生效&#xff0c;我们重启即可。 电脑重启后&#xff0c;打开Microsoft Store搜索WSL&#xff0c;既可以看到支持的操作系统&#xff0c;我们选择Ubuntu即可&#xff0c;我们选择第一个就可以。 随后我们打开&#xff0c;发现报…

第十三届蓝桥杯大赛软件赛省赛CC++大学B组

第十三届蓝桥杯大赛软件赛省赛CC 大学 B 组 文章目录 第十三届蓝桥杯大赛软件赛省赛CC 大学 B 组1、九进制转十进制2、顺子日期3、刷题统计4、修建灌木5、x进制减法6、统计子矩阵7、积木画8、扫雷9、李白打酒加强版10、砍竹子 1、九进制转十进制 计算器计算即可。2999292。 2、…

学习网安(19)

防火墙——安全产品 功能&#xff1a; 杀毒&#xff1a; 针对病毒&#xff0c;特征篡改系统中的文件 杀毒软件针对处理病毒程序 防火墙&#xff1a; 针对木马&#xff0c;特征系统窃密 防火墙针对处理木马 种类&#xff1a; 硬件防火墙&#xff1a; 各个网络安全厂商…