这是一棵适合搜索二叉树

🎈个人主页:🎈 :✨✨✨初阶牛✨✨✨
🐻强烈推荐优质专栏: 🍔🍟🌯C++的世界(持续更新中)
🐻推荐专栏1: 🍔🍟🌯C语言初阶
🐻推荐专栏2: 🍔🍟🌯C语言进阶
🔑个人信条: 🌵知行合一
🍉本篇简介:>:"二叉搜索树"的模拟实现
金句分享:
✨远赴人间惊鸿宴,一睹人间盛世颜!✨

目录

  • 一、什么是"二叉搜索树"?
  • 二、"二叉搜索树"的实现
    • 结点结构
    • "二叉搜索树":的结构
    • (1) "插入"操作
    • (2) "查找"操作
    • (3) "删除"操作 (重点)
    • (4)"中序"遍历
  • 三、结语

一、什么是"二叉搜索树"?

二叉搜索树(Binary Search Tree)又称为二叉查找树,是一种常用的数据结构。它是一棵空树,或者是具有以下性质的二叉树:

  1. 左子树上所有结点的值都小于它的根结点的值。
  2. 右子树上所有结点的值都大于它的根结点的值。
  3. 左右子树也分别为二叉搜索树。

错误示例1:
在这里插入图片描述
错误示例2:
在这里插入图片描述

正确示例:
在这里插入图片描述

二、"二叉搜索树"的实现

本篇文章实现的是键值对也就是(key,value)版本的 “二叉搜索树”.
Key-value版本的二叉搜索树(BST)是一种基于二叉树数据结构的数据结构,其中每个节点都存储一个键-值对。在该数据结构中,每个节点都具有一个唯一的关键字,该关键字用于对节点进行排序.

如下是树中每个结点的结构:

结点结构

template<class K, class V>
struct BSTreeNode
{BSTreeNode(const K& key = K(), const V& value = V()): _left(nullptr), _right(nullptr), _key(key), _value(value){}BSTreeNode<K,V>* _left;BSTreeNode<K,V>* _right;K _key;							//关键码,用于比较大小的,按key比较V _value;						//用于存储数据
};

“二叉搜索树”:的结构

template<class K, class V>
class BSTree
{typedef BSTreeNode<K, V> Node;		//注意这里的类型重命名
public:bool Insert(const K& key, const V& value);Node* Find(const K& key);bool Erase(const K& key);void _InOrder(Node* root);void InOrder();
private:Node* _root = nullptr;
};

(1) "插入"操作

根据"二叉搜索树"的特性,我们不难知道,左子树 < 根 < 右子树.

  1. 如果是空树,则表明新插入的结点将作为根节点.
  2. 如果不是空树,则先找到该插入的位置,再链接即可.

示例:如果在插入一个结点值为9的结点.

寻找过程:
比根节点8大,所以往右找.
12小,所以往左找.
11小,所以往左找.
11的左边为空,寻找结束.

9结点插入到11的左边.

在这里插入图片描述

//插入操作
template<class K, class V>
bool BSTree<K,V>::Insert(const K& key, const V& value)
{//如果是空树,则直接赋值给根节点if (_root == nullptr){//没看清node结构的,可以翻到上面在看一下构造函数._root = new Node(key,value);	//用值构建结点,并赋给根节点return true;}//如果不是 空树Node* cur = _root;			//代替根节点遍历树,寻找插入位置.Node* pnode = nullptr;		//记录目标位置的父亲结点while (cur)				//一直找到nullptr为止{pnode = cur;				//更新父节点if (key > cur->_key)		//如果插入的键值对 key 比当前元素的key大,则往右走{cur = cur->_right;}else if (key < cur->_key)		//如果插入的值比当前元素小,则往左走{cur = cur->_left;}else return false;			//相等则返回false}//判断插入在左边还是右边Node* newnode = new Node(key, value);if (key < pnode->_key){pnode->_left = newnode;}else{pnode->_right = newnode;}return true;
}

(2) "查找"操作

友友们插入操作都能轻松拿捏,那find还不是轻松拿捏?

小注意:
如果函数是在类里面声明,类外面定义,则需要注意一个小问题.
Node是一个类型还是一个变量(静态成员变量可以通过类名+ ::访问),所以需要在前面加上一个关键字typename ,告诉编译器这是一个类型.

template<class K, class V>
typename BSTree<K,V>::Node* BSTree<K, V>::Find(const K& key)
{Node* cur = _root;			//代替根节点遍历树.while (cur){if (key > cur->_key)		//如果查找的值比当前元素大,则往右走{cur = cur->_right;}else if (key < cur->_key)		//如果查找的值比当前元素小,则往左走{cur = cur->_left;}else return cur;			//相等则说明找到了}return nullptr;
}

(3) "删除"操作 (重点)

删除操作应该是"二叉搜索树"最难的操作了,这里牛牛就讲解的仔细一点吧!

(1)情况1: 目标结点没有孩子.
处理方法:找到该结点 和 该结点的父亲,直接删除即可.
在这里插入图片描述

(2)情况2:目标结点只有一个孩子,可能是左孩子,也可能是右孩子.
处理方法:
只有左孩子时:
让父亲不再指向自己(这里要判断自己在父亲的左还是右),而是指向自己的左孩子,然后再删除自己.
在这里插入图片描述

只有右孩子时:
让父亲不再指向自己(这里要判断自己在父亲的左还是右),而是指向自己的右孩子,然后再删除自己.
在这里插入图片描述

情况3: 目标结点有两个孩子.

右子树最小节点:
在这里插入图片描述

左子树最大节点:
在这里插入图片描述

代码实现:

template<class K, class V>
bool BSTree<K, V>::Erase(const K& key)
{if (_root == nullptr){cout << "空树不可删除" << endl;//空树无法删除return false;}//寻找目标结点的位置Node* pnode = nullptr;		//记录当前结点的父亲结点Node* cur = _root;			//当前结点:代替根节点遍历树.//寻找目标结点while (cur){if (key > cur->_key)		//如果插入的值比当前元素大,则往右走{pnode = cur;cur = cur->_right;}else if (key < cur->_key)		//如果插入的值比当前元素小,则往左走{pnode = cur;cur = cur->_left;}else  break;			//相等则说明找到了}//表示在树中 未找到if (cur == nullptr) { return false; }//这里采取与右子树的最小结点替换的方法if (cur->_right && cur->_left)//如果有两个孩子{Node* p_left_max = nullptr;			//右树 的最小节点的父亲Node* left_max = cur->_right;		//右树 的最小节点//寻找目标结点 右树 的最小节点while (left_max->_left){p_left_max = left_max;left_max = left_max->_left;}//替换cur->_key = left_max->_key;		//其实覆盖即可cur->_value = left_max->_value;//将原右子树的最小结点的父亲与 右树最小结点断绝关系p_left_max->_left = nullptr;delete left_max;					//删除右树最小结点return true;}// 要删除的节点只有一个子节点或没有子节点Node* child = nullptr;//这样child就是孩子if (cur->_left)	//只有左孩子{child = cur->_left;}else//只有右孩子或者没有孩子{child = cur->_right;}if (pnode == nullptr) // 根节点要删除的情况_root = child;else if (pnode->_left == cur) // 要删除的节点是父节点的左子节点pnode->_left = child;else // 要删除的节点是父节点的右子节点pnode->_right = child;delete cur;return true;
}

(4)"中序"遍历

学过二叉树的友友,对于这个,没啥好说的吧.

补充小技巧.

由于我们在类外面调用中序遍历函数需要传递root结点,但是root结点是私有成员变量,在类外面无法获取.
对象名.InOrder();

优秀的解决方法:
再嵌套一层,类里面的函数可以直接获取私有成员变量root,所以我们可以利用这一点.

template<class K, class V>
void  BSTree<K, V>::InOrder()
{if (_root == nullptr){cout << "空树" << endl;return;}_InOrder(_root);		//这里调用即可
}

类中:

template<class K, class V>
class BSTree
{typedef BSTreeNode<K, V> Node;
public:void _InOrder(Node* root);void InOrder();
private:Node* _root = nullptr;
};

真正的中序遍历:


template<class K, class V>
void  BSTree<K, V>::_InOrder(Node* root)
{if (root == nullptr)return;_InOrder(root->_left);cout << root->_key << "->" << root->_value << endl;_InOrder(root->_right);
}

三、结语

好的,到这里二叉搜索树就实现完毕了,二叉搜索树可是很优秀的一种数据结构呢!
搜索数据的时间复杂度在O(logn)级别,因为每判断一次,就可以舍去一半的子树(大往右子树找,小往左子树找),这样就是高度层.

当然,搜索二叉树也是有明显的缺点的,到时候我们在AVL树中介绍吧!

在这里插入图片描述

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

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

相关文章

单链表OJ题--9.环形链表

9.环形链表 141. 环形链表 - 力扣&#xff08;LeetCode&#xff09; /* 解题思路&#xff1a; 定义快慢指针fast,slow, 如果链表确实有环&#xff0c;fast指针一定会在环内追上slow指针。 */typedef struct ListNode Node; bool hasCycle(struct ListNode *head) {Node* slow …

深信服技术认证“SCSA-S”划重点:渗透测试工具使用

为帮助大家更加系统化的学习网络安全知识&#xff0c;尽快通过深信服安全服务认证工程师认证&#xff0c;深信服推出“SCSA-S认证备考秘笈”共十期内容&#xff0c;“考试重点”内容框架&#xff0c;帮助大家快速get重点知识~ 划重点来啦 深信服安全服务认证工程师&#xff08;…

【开源】基于Vue和SpringBoot的创意工坊双创管理系统

项目编号&#xff1a; S 049 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S049&#xff0c;文末获取源码。} 项目编号&#xff1a;S049&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 管理员端2.2 Web 端2.3 移动端 三、…

紧跟热点:教你如何快速掌握ChatGPT

2023年随着OpenAI开发者大会的召开&#xff0c;最重磅更新当属GPTs&#xff0c;多模态API&#xff0c;未来自定义专属的GPT。微软创始人比尔盖茨称ChatGPT的出现有着重大历史意义&#xff0c;不亚于互联网和个人电脑的问世。360创始人周鸿祎认为未来各行各业如果不能搭上这班车…

图解Spark Graphx基于connectedComponents函数实现连通图底层原理

原创/朱季谦 第一次写这么长的graphx源码解读&#xff0c;还是比较晦涩&#xff0c;有较多不足之处&#xff0c;争取改进。 一、连通图说明 连通图是指图中的任意两个顶点之间都存在路径相连而组成的一个子图。 用一个图来说明&#xff0c;例如&#xff0c;下面这个叫graph…

【教3妹学编程-算法题】最大异或乘积

3妹&#xff1a;2哥&#xff0c;你有没有看到新闻“18岁父亲为4岁儿子落户现身亲子鉴定” 2哥 : 啥&#xff1f;18岁就当爹啦&#xff1f; 3妹&#xff1a;确切的说是14岁好吧。 2哥 : 哎&#xff0c;想我30了&#xff0c; 还是个单身狗。 3妹&#xff1a;别急啊&#xff0c; 2…

已完结7个,再启动1个新项目,嘎嘎强!

作者&#xff1a;小傅哥 博客&#xff1a;https://bugstack.cn 沉淀、分享、成长&#xff0c;让自己和他人都能有所收获&#xff01;&#x1f604; 大家好&#xff0c;我是技术UP主小傅哥。 &#x1f490;又到了启动新项目的时候&#xff0c;死鬼开心嘛。小傅哥的星球&#xf…

数据库课后习题加真题

文章目录 第二章第三章第四到六章某年真题 第二章 第三章 3.8 对于教学数据库的三个基本表&#xff1a; s( 学号 ‾ \underline{学号} 学号​&#xff0c;姓名&#xff0c;年龄, 性别) sc( 学号 , 课程号 ‾ \underline{学号, 课程号} 学号,课程号​, 成绩) c( 课程号 ‾ \un…

【C++】类与对象(中)

一、类的默认成员函数 如果一个类中什么成员都没有&#xff0c;简称为空类。 空类中真的什么都没有吗&#xff1f;并不是&#xff0c;任何类在什么都不写时&#xff0c;编译器会自动生成以下6个默认成员函数。 默认成员函数&#xff1a;用户没有显式实现&#xff0c;编译器会自…

[超详细]基于YOLO&OpenCV的人流量统计监测系统(源码&部署教程)

1.图片识别 2.视频识别 [YOLOv7]基于YOLO&#xff06;Deepsort的人流量统计系统(源码&#xff06;部署教程)_哔哩哔哩_bilibili 3.Deepsort目标追踪 &#xff08;1&#xff09;获取原始视频帧 &#xff08;2&#xff09;利用目标检测器对视频帧中的目标进行检测 &#xff08…

oracle21c报错 【ORA-65096: 公用用户名或角色名无效】

1.数据库版本 oracle21c 2.问题提示 创建用户提示【ORA-65096: 公用用户名或角色名无效】 create user 自定义用户名 identified by 密码;--例:用户为test1&#xff0c;密码为123456 create user test1 identified by 123456;三.解决办法及结果 oracle11g之后的版本&#xff…

将kali系统放在U盘中插入电脑直接进入kali系统

首先准备一个空白的 U 盘。 Kali Linux | Penetration Testing and Ethical Hacking Linux Distribution 在 Windows 上制作 Kali 可启动 USB 驱动器 Making a Kali Bootable USB Drive on Windows | Kali Linux Documentation 1. 首先下载 .iso 镜像 Index of /kali-images…

构建 App 的方法

目录 构建 App 使用 App 设计工具以交互方式构建 App 使用 MATLAB 函数以编程方式构建 App 构建实时编辑器任务 可以使用 MATLAB 来构建可以集成到各种环境中的交互式用户界面。可以构建两种类型的用户界面&#xff1a; App - 基于用户交互执行操作的自包含界面 实时编辑器…

【HCSD大咖直播】亲授大厂面试秘诀【云驻共创】

同学们&#xff0c;毕业季是否找到了自己心仪的工作呢&#xff1f;是否了解大厂面试流程、要求以及技巧呢&#xff1f;华为云IoT高级工程师&#xff0c;传授大厂面试秘诀&#xff0c;教大家如何轻松get大厂offer&#xff01;提前为大厂面试做准备&#xff0c;赢在起跑线&#x…

uniapp和vue3+ts创建自定义下拉选择框组件

使用uniapp开发小程序的时候&#xff0c;使用了uview的ui组件&#xff0c;但是里面没有下拉选择组件&#xff0c;只有Picker 选择器&#xff0c;但是我们想要使用下拉选择的组件&#xff0c;所以需要自定义个一个下拉选择的自定义组件&#xff0c;我就只能自己动手创建这个自定…

31、Flink的SQL Gateway介绍及示例

Flink 系列文章 1、Flink 部署、概念介绍、source、transformation、sink使用示例、四大基石介绍和示例等系列综合文章链接 13、Flink 的table api与sql的基本概念、通用api介绍及入门示例 14、Flink 的table api与sql之数据类型: 内置数据类型以及它们的属性 15、Flink 的ta…

梳理一名Go后端程序员日常用的软件~

大家好&#xff0c;我是豆小匠。 这期分享下我日常工作用到的软件和工具&#xff01; 省流版图片↓↓↓ 工具分为四类&#xff1a;编码软件、笔记/文档软件、开发工具和日常软件等。 1. 编码软件 1.1. Goland 出自JetBrain家族&#xff0c;IDE的王者&#xff0c;作为我的…

Vuetify:定制化、响应式的 Vue UI 库 | 开源日报 No.83

vuetifyjs/vuetify Stars: 38.1k License: MIT Vuetify 是一个无需设计技能的 UI 库&#xff0c;具有精美手工制作的 Vue 组件。它具有以下核心优势和主要功能&#xff1a; 可定制性&#xff1a;使用 SASS/SCSS 进行广泛自定义&#xff0c;并提供默认配置和蓝图。响应式布局&…

debian10 开启rdp安装firefox并解决firefox 中文乱码

debian10 开启rdp安装firefox apt -y install tigervnc-standalone-server apt -y install xrdp tigervnc-standalone-server systemctl enable xrdp --nowapt install firefox-esrmstsc连接 firefox-settings-general-fonts-advanced-Simplified Chinese

如何看待Unity新收费模式?

Unity新收费模式的变化主要在于将收费重心从功能分级收费转变为资源使用量收费&#xff0c;这个改变已经引起了一定的争议和反响。以下是我个人的看法&#xff1a; 优点&#xff1a; 更公平的收费方式&#xff1a;新的收费模式将更加公平&#xff0c;用户只需按照实际使用的数…