C++——二叉树

引入

 map和set特性需要先铺垫二叉搜索树,而二叉搜索树也是一种树形结构
 二叉搜索树的特性了解,有助于更好的理解map和set的特性

1.二叉搜索树的概念及优缺点

1.1二叉搜索树的概念

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

左子树的值<根的值<右子树的值

int a[] = {8, 3, 1, 10, 6, 4, 7, 14, 13};

搜索二叉树通过二分查找可以轻松找到目标值,避免了暴力遍历的方式。

根据搜索二叉树的性质,目标值会出现在左子树,右子树则是比目标值大的值。因此,搜索二叉树查找一个值的最坏情况只需要查找树的高度次 

既然提到了最坏情况,那么搜索二叉树的时间复杂度是多少呢?

搜索二叉树的增删查改的时间复杂度实际上是O(N),因为这棵树有可能会蜕化成一个 "单边树"(也就是只往一边存,另外一边没有节点)。

 1.2二叉搜索树的部分使用及其实现

 二叉搜索树的查找

从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找。
最多查找高度次,走到到空,还没找到,这个值不存在。
//查找bool Find(const K& key){Node* cur = _root;//从根开始遍历,走到空说明找不到while (cur){if (cur->_key < key){//遍历的值比要找的值小,往右走cur = cur->_right;}else if (cur->_key > key){//遍历的值比要找的值大,往左走cur = cur->_left;}else{//走到这说明找到了return true;}}//while循环走到头(遇到空节点)那就说明没找到return false;}

二叉搜索树的插入

插入的具体过程如下:
树为空,则直接新增节点,赋值给root指针
树不空,按二叉搜索树性质查找插入位置,插入新节点

 

 

	bool Insert(const K& key){//为空节点的话直接插入if (_root == nullptr){_root = new Node(key);return true;}//复用一下上面的查找//走到空节点(出循环)就可以插入了//需要一个父指针,去连接那个新节点Node* parent = nullptr;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 = new Node(key);//看情况把它接到父节点的左边还是右边if (parent->_key < key){parent->_right = cur;}else{parent->_left = cur;}return true;}

 

二叉搜索树的删除

首先查找元素是否在二叉搜索树中,如果不存在,则返回,
否则要删除的结点可能分下面四种情 况:

要删除的结点无孩子结点

要删除的结点只有左孩子结点

要删除的结点只有右孩子结点

要删除的结点有左、右孩子结点

看起来有待删除节点有4中情况,实际情况无孩子可以与情况只有左或者只有右合并起来
因此真正的删除过程如下:
无孩子节点也就是叶子节点
删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点--直接删除
删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点--直接删除
替换法删除:找一个能替换我的节点,交换值转换删除他
右两种替换方法:1.左子树的最大(最右)节点 2.右子树的最小(最左)节点

 

如果原本4节点还有还记记得托付给其父节点 (由于替换法删除是取左子树的最右(右子树的最左)节点,所以哪怕4节点还有子节点,那也只能有一个),比如:

 

	//删除bool Erase(const K& key){Node* parent = nullptr;Node* cur = _root;//先找到要删的那个值while (cur){if (cur->_key < key){//cur走parent也要走parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else{//找到了,准备删除//要删除的节点的左孩子节点为空if (cur->_left == nullptr){if (cur == _root){//要删的值在根节点,那直接把根节点给右节点就行了(左为空嘛)_root = cur->_right;}else{if (cur == parent->_right){//cur左孩子节点为空,若cur在其父节点的右边,那么其右孩子一定大于cur的父节点//右边托付parent->_right = cur->_right;}else{//左边托付parent->_left = cur->_right;}}//托付完删除delete cur;return true;}//要删除的节点的右孩子节点为空else if(cur->_right == nullptr){if (cur == _root){//要删的值在根节点,那直接把根节点给左节点就行了_root = cur->_left;}else{if (cur == parent->_right){//若cur在其父节点的右边,那么其左孩子一定大于于cur的父节点parent->_right = cur->_left;}else{//左边托付parent->_left = cur->_left;}}//托付完删除delete cur;return true;}else{//cur左右都非空Node* rightMin = cur->_right;//右边最小节点Node* rightMinParent = cur;//右边最小节点的父节点//找右边的最小节点while (rightMin->_left){rightMinParent = rightMin;rightMin = rightMin->_left;}//找到就替换一下cur->_key = rightMin->_key;//最小节点的孩子得判断是rightMin父亲的哪一边接收if (rightMin == rightMinParent->_left){rightMinParent->_left = rightMin->_right;}else{rightMinParent->_right = rightMin->_right;}delete rightMin;return true;}}}//找不到return false;}

 2.二叉搜索树的实现

2.1上述补充

节点

template<class K>
struct BSTreeNode
{typedef BSTreeNode<K> Node;Node* _left;Node* _right;K _key;BSTreeNode(const K& key):_left(nullptr),_right(nullptr),_key(key){}
};

二叉树的 


template<class K>
struct BSTree
{typedef BSTreeNode<K> Node;
public://强制生成默认构造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);}BSTree<K>& operator=(BSTree<K> t){swap(_root, t._root);return *this;}~BSTree(){Destroy(_root);}void Destroy(Node* root)//后序遍历析构(销毁){if (root == nullptr){return;}Destroy(root->_left);Destroy(root->_right);delete root;}private:Node* _root = nullptr;
};

2.2查找、插入、删除递归实现

查找递归

因为无法传递根节点,所以要封装一层。

bool FindR(const K& key){return _FindR(_root, key);}bool _FindR(Node* root, const K& key){if (root == nullptr)return false;if (root->_key < key){return _FindR(root->_right, key);}else if (root->_key > key){return _FindR(root->_left, key);}else{return true;

插入递归

注意:插入的值如何与父节点连接

可以在传(递归)的根节点上加引用,这样每深入一层,那么这层的root就是上一场root->left(right)的引用,也就是说最后一层他会自动连接新节点

	bool InsertR(const K& key){return _InsertR(_root, key);}bool _InsertR(Node*& root, const K& key){if (root == nullptr){root = new Node(key);return true;}if (root->_key < key){return _InsertR(root->_right, key);}else if (root->_key > key){return _InsertR(root->_left, key);}else{return false;}}

删除递归

这里的引用和上面一样

	bool EraseR(const K& key){return _EraseR(_root, key);}bool _EraseR(Node*& root, const K& key){if (root == nullptr)return false;if (root->_key < key){return _EraseR(root->_right, key);}else if (root->_key > key){return _EraseR(root->_left, key);}else{Node* del = root;if (root->_right == nullptr){root = root->_left;}else if (root->_left == nullptr){root = root->_right;}else{Node* rightMin = root->_right;while (rightMin->_left){rightMin = rightMin->_left;}swap(root->_key, rightMin->_key);return _EraseR(root->_right, key);}delete del;return true;}}

3. 搜索二叉树的应用

3.1K模型

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

3.2kv模型

KV模型:每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对。该种方
式在现实生活中非常常见:
比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英
文单词与其对应的中文<word, chinese>就构成一种键值对;
再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出
现次数就是<word, count>就构成一种键值对

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

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

相关文章

windows安装sqlite

windows安装sqlite比linux麻烦很多 1.下载 下载链接&#xff1a;链接 下载dll的zip文件 2.解压并放到文件夹 将压缩包内的文件解压&#xff0c;放到C://sqlite下 3.编辑环境变量 添加到系统变量的path中 4.验证 打开命令提示符&#xff0c;输入 sqlite3有结果就行

DNS 域名系统——应用层

目录 1 域名系统 DNS 1.1 域名系统 1.2 互联网的域名结构 1.2.1 顶级域名 TLD(Top Level Domain) (1) 国家顶级域名 nTLD (2) 通用顶级域名 gTLD (3) 基础结构域名 (infrastructure domain) 1.3 域名服务器 1.3.1 域名服务器的四种类型 &#xff08;1…

深入理解java之多线程(一)

前言&#xff1a; 本章节我们将开始学习多线程&#xff0c;多线程是一个很重要的知识点&#xff0c;他在我们实际开发中应用广泛并且基础&#xff0c;可以说掌握多线程编写程序是每一个程序员都应当必备的技能&#xff0c;很多小伙伴也会吐槽多线程比较难&#xff0c;但因为其实…

【linux系统体验】-archlinux折腾日记

archlinux 一、系统安装二、系统配置及美化2.1 中文输入法2.2 安装virtualbox增强工具2.3 终端美化 三、问题总结3.1 终端中文乱码 一、系统安装 安装步骤人们已经总结了很多很全: Arch Linux图文安装教程 大体步骤&#xff1a; 磁盘分区安装 Linux内核配置系统&#xff08;…

面向数据报编程-UDP协议

目录 前言&#xff1a; 1.UDP协议API 1.1UDP编程原理 1.2DatagramSocket类 &#xff08;1&#xff09;DatagramSocket构造方法 &#xff08;2&#xff09;DatagramSocket普通方法 1.3DatagramPacket类 &#xff08;1&#xff09;DatagramPacket构造方法 &#xff08;2…

今日早报 每日精选15条新闻简报 每天一分钟 知晓天下事 2月10日,星期六

每天一分钟&#xff0c;知晓天下事&#xff01; 2024年2月10日 星期六 农历正月初一 春节 1、 国务院&#xff1a;到2025年&#xff0c;初步建成覆盖各领域、各环节的废弃物循环利用体系。 2、 国家移民管理局&#xff1a;部分国家人员可以用更多事由免签入境海南。 3、 市场…

OpenAI给DALL-E 3来了个新动作,加入了全新水印技术

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

政安晨:政安晨:机器学习快速入门(三){pandas与scikit-learn} {模型验证及欠拟合与过拟合}

这一篇中&#xff0c;咱们使用Pandas与Scikit-liarn工具进行一下模型验证&#xff0c;之后再顺势了解一些过拟合与欠拟合&#xff0c;这是您逐渐深入机器学习的开始&#xff01; 模型验证 评估您的模型性能&#xff0c;以便测试和比较其他选择。 在上一篇中&#xff0c;您已经…

51单片机 跑马灯

#include <reg52.h>//毫秒级延时函数 void delay(int z) {int x,y;for(x z; x > 0; x--)for(y 114; y > 0 ; y--); }sbit LED1 P1^0x0; sbit LED2 P1^0x1; sbit LED3 P1^0x2; sbit LED4 P1^0x3; sbit LED5 P1^0x4; sbit LED6 P1^0x5; sbit LED7 P1^0x6; s…

详解格式化输入函数scanf

大家好&#xff0c;今天给大家介绍详解格式化输入函数scanf&#xff0c;文章末尾附有分享大家一个资料包&#xff0c;差不多150多G。里面学习内容、面经、项目都比较新也比较全&#xff01;可进群免费领取。 C语言中常用的输入可以有多种方式&#xff0c;如scanf(),getchar(),g…

猫头虎分享:2024龙年IT行业热门技术大全

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

【06】C++ 模板初阶

文章目录 &#x1f308; Ⅰ 泛型编程&#x1f308; Ⅱ 函数模板1. 函数模板概念2. 函数模板格式 &#x1f308; Ⅰ 泛型编程 1. 泛型编程引入 假设当前要实现交换两个变量的功能&#xff0c;那么就得根据实参的数据类型来对该函数进行重载。重载的函数只是数据类型不同而已&a…

Select 选择器 el-option 回显错误 value

离谱 回显的内容不是 label 而是 value 的值 返回官方看说明&#xff1a; v-model的值为当前被选中的el-option的 value 属性值 value / v-model 绑定值有3种类型 boolean / string / number 根据自身代码猜测是&#xff1a;tableData.bookId 与 item.id 类型不一致导致 &…

火车可视化调车系统

列车在调车作业时&#xff0c;当机车头在尾部推动车厢时&#xff0c;司机室一人操控机车&#xff0c;车厢前端配备两名挂梯随车运行调车员&#xff0c;调车员人为分析行车方向是否有障碍、轨道行人等紧急情况&#xff0c;通过对讲机通知司机控制停车。由于司机无法直观观察列车…

ICCV 2023 | 8篇论文看扩散模型diffusion用于图像检测任务:动作检测、目标检测、异常检测、deepfake检测...

1、动作检测 DiffTAD: Temporal Action Detection with Proposal Denoising Diffusion 基于扩散方法提出一种新的时序动作检测&#xff08;TAD&#xff09;算法&#xff0c;简称DiffTAD。以随机时序proposals作为输入&#xff0c;可以在未修剪的长视频中准确生成动作proposals。…

Python环境下基于指数退化模型和LSTM自编码器的轴承剩余寿命预测

滚动轴承是机械设备中关键的零部件之一&#xff0c;其可靠性直接影响了设备的性能&#xff0c;所以对滚动轴承的剩余使用寿命(RUL)进行预测是十分必要的。目前&#xff0c;如何准确地对滚动轴承剩余使用寿命进行预测&#xff0c;仍是一个具有挑战的课题。对滚动轴承剩余寿命评估…

探索Xposed框架:个性定制你的Android体验

探索Xposed框架&#xff1a;个性定制你的Android体验 1. 引言 在当今移动设备市场中&#xff0c;Android系统作为最受欢迎的操作系统之一&#xff0c;其开放性和可定制性备受用户青睐。用户希望能够根据个人喜好和需求对其设备进行定制&#xff0c;以获得更符合自己习惯的使用…

python 自我检测题--part 1

1. Which way among them is used to create an event loop ? Window.mainloop() 2. Suppose we have a set a {10,9,8,7}, and we execute a.remove(14) what will happen ? Key error is raised. The remove() method removes the specified element from the set. Th…

涤生大数据实战:基于Flink+ODPS历史累计计算项目分析与优化(上)

涤生大数据实战&#xff1a;基于FlinkODPS历史累计计算项目分析与优化&#xff08;一&#xff09; 1.前置知识 ODPS&#xff08;Open Data Platform and Service&#xff09;是阿里云自研的一体化大数据计算平台和数据仓库产品&#xff0c;在集团内部离线作为离线数据处理和存…

【八大排序】归并排序 | 计数排序 + 图文详解!!

&#x1f4f7; 江池俊&#xff1a; 个人主页 &#x1f525;个人专栏&#xff1a; ✅数据结构冒险记 ✅C语言进阶之路 &#x1f305; 有航道的人&#xff0c;再渺小也不会迷途。 文章目录 一、归并排序1.1 基本思想 动图演示2.2 递归版本代码实现 算法步骤2.3 非递归版本代…