二叉搜索树相关

二叉搜索树

  • 定义:
  • 对二叉搜索树的一些操作
    • 基本结构
    • Insert操作
    • Find操作
    • Erase操作
  • InOrder遍历二叉树操作
  • 模拟字典
  • 模拟统计次数

定义:

二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:若它的左子树不为空,则左子树上所有节点的值都小于根节点的值。 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值。 它的左右子树也分别为二叉搜索树

对二叉搜索树的一些操作

基本结构

首先,我们先定义出一些结构
第一个结构是树里面的内容,包含左指针右指针,以及key和value。
接下来的一个类,用来表示这是一个二叉搜索树

template<class K, class V>
struct BSTreeNode
{BSTreeNode<K, V>* _left;BSTreeNode<K, V>* _right;K _key;V _value;BSTreeNode(const K& key, const V& value):_left(nullptr),_right(nullptr),_key(key),_value(value){}
};template<class K, class V>
class BSTree
{typedef BSTreeNode<K, V> Node;
public:Node* _root = nullptr;
};

Insert操作

Insert的思路是通过key去查找,看在树里面有没有这个节点,如果有就插入失败(因为二叉搜索树中不允许有重复的值出现,否则就失去了意义)。 如果没有那就去找一个合适的位置插入。
找合适的位置:①如果根为空,直接创建新节点当根节点插入 ②根不为空,就去子树上找。 这里我们要注意为了方便控制我们的循环条件,我们大循环是以cur为循环条件去找的,所以我们需要有parent来记录cur的父亲位置,这样后面才方便插入。(为什么不直接改循环条件? 因为这里判断不仅仅是一个节点,它既需要判断左节点又要判断右节点,所以我们选择以parent的形式去记录)

bool Insert(const K& key, const V& value)
{// 1.根为空,直接插入一个新节点if (_root == nullptr){_root = new Node(key, value);return true;}// 2.根不为空,则去找到合适位置插入Node* parent = _root;Node* cur = _root;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else return false;   // 如果找到相同的值那么插入失败了}// 此时,走出循环说明已经找到了插入位置,但此刻的cur已经是空指针了,我们应该需要的是cur的parent位置,所以上面需要有parent来记录cur = new Node(key, value);   // 直接重新赋值cur就可以了,不用再加一个新的变量进来if (key > parent->_key)parent->_right = cur;if (key < parent->_key)parent->_left = cur;return true;
}

Find操作

Find操作非常简单,找根,左孩子右孩子,如果遍历完了依然没有找到,那就返回空指针。

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

Erase操作

Erase操作相对就会比较的麻烦了。
Erase的思路:首先,我们需要去判断这个节点是否存在,不存在返回false。存在才会有下面的删除操作(不存在的情况放在最外面了)。
删除节点分为三个情况:①删除的节点只有左孩子
②删除节点只有右孩子(没有孩子包含在这两种情况)
③既有左孩子又有右孩子
那么针对三种情况,我们有不同的要求,建议通过图形来进行理解:
①、②种是类似的。在这里插入图片描述
我们进行判断又分三种情况,第一种:我们要删除的节点就是根节点,那么就直接对根进行处理(我们说了删除节点无左右孩子也包含在①、②的情况里,这里就体现出来了,这里也可以处理)。 第二种:我们要删除的节点cur是parent的左孩子 第三种: 我们要删除的节点cur是parent的右孩子。 第二种和第三种情况是不一样的,这里结合图片就可以看出来。

难处理的是第③种情况
如果说我们删除的节点既有左孩子又有右孩子,那么在处理的时候需要用到交换法。 交换的对象是cur和cur左孩子中的最大值或右孩子中的最小值。
所以第一步是找到最大的左孩子或最小的右孩子(这里我们以找到最小右孩子为例)
找到之后,还要分情况讨论: 一种是这个最小的右孩子有左节点也有右节点的情况
还一种是这个最小的右孩子只有右节点的情况(注意:这里不可能出现这个节点还有左节点的情况,因为在此之前我们找到的这个RightMin是右子树的最小节点,就不可能出现它还有比他小的左节点的情况)
第二种可以看到,我们的RightMin还有一个右节点的情况,这个情况就得特殊处理了,如果RightMinParent->left = RightMin。我们RightMinParent的左节点就得指向RightMin的右结点(同样,RightMin不会有左节点)
这里我们怎么样更好的理解记忆呢?
我们可以看第一种情况是RightMinParent和RightMin二者成一条线。 第二种情况是不成一条线,RightMin为RightMinParent左孩子的情况。
在这里插入图片描述

bool Erase(const K& key)
{// 删除分为3个情况// 1.删除节点只有左孩子 2.删除节点只有右孩子(没有孩子包含在这两种情况)  3.既有左孩子又有右孩子Node* parent = _root;Node* cur = _root;while (cur){// 先找节点,找到了才有后面的删除操作,否则要返回falseif (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;else if(cur == parent->_left)   // 这个节点为parent的左孩子的情况{parent->_left = cur->_right;}else if (cur == parent->_right) // 这个节点为parent的右孩子的情况{parent->_right = cur->_right;}delete cur;}else if (cur->_right == nullptr)  // 无右孩子{if (cur == _root)_root = cur->_left;  // 直接让左孩子作为根就可以了else if (cur == parent->_left){parent->_left = cur->_left;}else if (cur == parent->_right){parent->_right = cur->_left;}delete cur;}else  // 此时为左右孩子都有的情况{Node* RightMinParent = cur;Node* RightMin = cur->_right;while (RightMin->_left){RightMinParent = RightMin;RightMin = RightMin->_left;}swap(RightMin->_key, cur->_key);swap(RightMin->_value, cur->_value);if (RightMinParent->_right == RightMin)   // 特殊情况得特殊处理RightMinParent->_right = RightMin->_right;elseRightMinParent->_left= RightMin->_right;  //此时,RightMin//只可能有右孩子,因为它是最小的右子树节点(不能有比它更小的了)delete RightMin;}}return true;}return false;
}

InOrder遍历二叉树操作

这里我们需要注意两个点: 第一个是如何遍历,也就是我们调用采用递归的方法。
第二个是为什么我们这里要封装一层,而不是直接用,直接传值root并接收。 因为root是一个私有变量,我们在类外是无法访问到的,此时我们要执行遍历操作又必须得从根开始。想访问到,就需要通过函数进入到内部,才能访问到这个私有变量。

	void InOrder()  // 为什么这里要封装一层而不是直接传入root,因为root是私有成员,//我们在外面传参时是访问不到这个私有成员变量的。我们只有通过函数进入到类内,//才能访问到root,并传参给包了一层的InOrder函数{_InOrder(_root);cout << endl;}
private:void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_key << ":" << root->_value << endl;_InOrder(root->_right);}

模拟字典

下面的代码可以用来模拟字典操作

BSTree<string, string> dict;
dict.Insert("insert", "插入");
dict.Insert("erase", "删除");
dict.Insert("left", "左边");
dict.Insert("string", "字符串");
dict.Erase("insert");
dict.Erase("string");
string str;
while (cin >> str)
{auto ret = dict.Find(str);if (ret){cout << str << ":" << ret->_value << endl;}else{cout << "单词拼写错误" << endl;}
}

模拟统计次数

下面的这个代码,可以用来统计次数

	string strs[] = { "苹果", "西瓜", "苹果", "樱桃", "苹果", "樱桃", "苹果", "樱桃", "苹果" };// 统计水果出现的次BSTree<string, int> countTree;for (auto str : strs){auto ret = countTree.Find(str);if (ret == NULL){countTree.Insert(str, 1);   // 不存在就插入}else{ret->_value++;  //存在就让value++}}countTree.InOrder();

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

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

相关文章

品鉴中的艺术表达:如何将红酒与绘画、雕塑等艺术形式相结合

品鉴雷盛红酒不仅是一种味觉的享受&#xff0c;更是一种艺术的体验。将雷盛红酒与绘画、雕塑等艺术形式相结合&#xff0c;能够创造出与众不同的审美体验&#xff0c;进一步丰富品鉴的内涵。 首先&#xff0c;绘画作为视觉艺术的一种表现形式&#xff0c;能够通过色彩和构图来传…

Linux:进程等待 进程替换

Linux&#xff1a;进程等待 & 进程替换 进程等待wait接口statuswaitpid接口 进程替换exec系列接口 当一个进程死亡后&#xff0c;会变成僵尸进程&#xff0c;此时进程的PCB被保留&#xff0c;等待父进程将该PCB回收。那么父进程要如何回收这个僵尸进程的PCB呢&#xff1f;父…

47.Redis学习笔记

小林coding -> 图解redis的学习笔记 文章目录 Rediswindwos安装docker安装redis启动redis使用RDM访问虚拟机中的redispython连接redis缓存穿透、击穿、雪崩基本数据类型高级数据类型高并发指标布隆过滤器分布式锁Redis 的有序集合底层为什么要用跳表&#xff0c;而不用平衡…

什么是 AI Agent ?

&#xff08;注&#xff1a;本文为小报童精选文章。已订阅小报童或加入知识星球「玉树芝兰」用户请勿重复付费&#xff09; 讲解的同时&#xff0c;也给你推荐一些实用的学习资源。 AI agent &#xff08;智能体 / 代理&#xff09;这个词儿最近非常流行&#xff0c;似乎「大语…

目标检测实战(八): 使用YOLOv7完成对图像的目标检测任务(从数据准备到训练测试部署的完整流程)

文章目录 一、目标检测介绍二、YOLOv7介绍三、源码/论文获取四、环境搭建4.1 环境检测 五、数据集准备六、 模型训练七、模型验证八、模型测试九、错误总结9.1 错误1-numpy jas mp attribute int9.2 错误2-测试代码未能跑出检测框9.3 错误3- Command git tag returned non-zero…

RabbitMQ高级(MQ的问题,消息可靠性,死信交换机,惰性队列,MQ集群)【详解】

目录 一、MQ的问题 1. 问题说明 2. 准备代码环境 1 创建project 2 创建生产者模块 3 创建消费者模块 二、消息可靠性 1. 介绍 2. 生产者确认机制 3. MQ消息持久化 4. 消费者确认机制 5. 消费者auto模式的失败重试 6. 小结 三、死信交换机和延迟消息 1. 介绍 2. …

【EasySpider】EasySpider+mysql执行配置异常

问题 使用易采集工具操作时候&#xff0c;遇到一个执行异常&#xff0c;后来发现没有选择数据类型 Loading stealth.min.js MySQL config file path: ./mysql_config.json 成功连接到数据库。 Successfully connected to the database. Traceback (most recent call last):…

湘潭大学数据库作业题完整答案

作业一&#xff1a; 考虑如下所示的关系数据库。这些关系上适当的主码是什么&#xff1f; 职工&#xff08;姓名&#xff0c;街道&#xff0c;城市&#xff09; 工作&#xff08;姓名&#xff0c;公司名&#xff0c;工资&#xff09; 公司&#xff08;公司名&#xff0c;城市&a…

45 套接字

本节重点 认识ip地址&#xff0c;端口号&#xff0c;网络字节序等网络编程中的基本概念 学习scoket&#xff0c;api的基本用法 能够实现一个简单的udp客户端/服务端 能够实现一个简单的tcp客户端/服务器&#xff08;但链接版本&#xff0c;多进程版本&#xff0c;多线程版本&a…

设计严谨,思路绝妙!这篇高级孟德尔随机化研究:药靶、共定位,发文一区(IF=8.9)!...

现在越来越多的学者在用孟德尔随机化高级方法发文&#xff0c;今天我们看的这篇这篇药靶孟德尔随机化&#xff0c;还用了共定位分析方法&#xff0c;亮点在于它的设计严谨&#xff0c;思路绝妙&#xff0c;一起看下去吧&#xff01; 2024年4月21日&#xff0c;四川大学华西医院…

(四)JVM实战——GC垃圾回收

垃圾回收算法 垃圾的判别 引用计数法&#xff1a;实现简单&#xff0c;判定效率高&#xff0c;回收没有延迟&#xff1b;无法解决循环引用的问题&#xff1b;可达性分析算法&#xff08;根搜索算法&#xff09;&#xff1a;没有循环引用的问题&#xff0c;防止内存泄漏 GCRo…

【挑战30天首通《谷粒商城》】-【第一天】03、简介-分布式基础概念

文章目录 课程介绍 ( 本章了解即可&#xff0c;可以略过)1、微服务简而言之: 2、集群&分布式&节点2.1、定义2.2、示例 3、远程调用4、负载均衡常见的负裁均衡算法: 5、服务注册/发现&注册中心6、配置中心7、服务熔断&服务降级7.1、服务熔断7.2、服务降级 8、AP…

纹理映射技术在AI去衣应用中的关键作用

引言&#xff1a; 随着人工智能技术的飞速发展&#xff0c;其在图像处理领域中的应用也日益广泛。AI去衣&#xff0c;作为一种颇具争议的技术应用&#xff0c;指的是利用深度学习算法自动移除或替换图片中的衣物。在这一过程中&#xff0c;纹理映射技术扮演了不可或缺的角色。本…

LLMs之GPT4ALL:GPT4ALL的简介、安装和使用方法、案例应用之详细攻略

LLMs之GPT4ALL&#xff1a;GPT4ALL的简介、安装和使用方法、案例应用之详细攻略 目录 GPT4ALL的简介 0、新功能 1、特点 2、功能 3、技术报告 GPT4ALL的安装和使用方法 1、安装 2、使用方法 GPT4ALL的案例应用 LLMs之LLaMA3&#xff1a;基于GPT4ALL框架对LLaMA-3实现…

数据结构-线性表-应用题-2.2-6

从有序顺序表中删除所有其值重复的元素&#xff0c;使表中的元素的值均不同 有序顺序表&#xff0c;值相同的元素一定在连续的位置上&#xff0c;初始时将第一个元素是为非重复的有序表&#xff0c;之后依次判断后面的元素是否与前面的非重复表的最后一个元素相同&#xff0c;…

JVM调参实践总结

JVM调优–理论篇从理论层面介绍了如何对JVM调优。这里再写一篇WIKI&#xff0c;尝试记录下JVM参数使用的最佳实践&#xff0c;注意&#xff0c;这里重点介绍HotSpot VM的调参&#xff0c;其他JVM的调参可以类比&#xff0c;但不可照搬。 Java版本选择 基于Java开发应用时&…

【Git】Git学习-10-11:GitHub,SHH配置,克隆仓库

学习视频链接&#xff1a;【GeekHour】一小时Git教程_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1HM411377j/?vd_source95dda35ac10d1ae6785cc7006f365780 创建仓库 配置SSH密钥可以更加安全&#xff0c;方便地推送、拉取代码 根目录下&#xff0c;进入.ssh文件&am…

gradio图像复原界面改进

图像复原界面展示需要输入图像和复原图像在界面的清晰对比&#xff0c;修改两张图像为同样大小。 默认情况&#xff1a; intreface代码如下&#xff1a; interface gr.Interface(fnrestore, # 要调用的函数inputs[gr.Image(label"输入图像")], # 第一个输入&am…

大数据Scala教程从入门到精通第三篇:Scala和Java的关系

一&#xff1a;Scala和Java的关系 1&#xff1a;详解 一般来说&#xff0c;学 Scala的人&#xff0c;都会 Java&#xff0c;而 Scala 是基于 Java 的&#xff0c;因此我们需要将 Scala和 Java 以及 JVM 之间的关系搞清楚&#xff0c;否则学习 Scala 你会蒙圈 Scala可以使用SDK…

构建 WebRTC 一对一信令服务器

构建 WebRTC 一对一信令服务器 构建 WebRTC 一对一信令服务器前言为什么选择 Nodejs&#xff1f;Nodejs 的基本原理浏览器使用 Nodejs安装 Nodejs 和 NPMsocket.io信令服务器搭建信令服务器客户端服务端启动服务器并测试 总结参考 构建 WebRTC 一对一信令服务器 前言 我们在学…