【C++】:搜索二叉树

朋友们、伙计们,我们又见面了,本期来给大家解读一下有关多态的知识点,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成!

C 语 言 专 栏:C语言:从入门到精通

数据结构专栏:数据结构

个  人  主  页 :stackY、

C + + 专 栏   :C++

Linux 专 栏  :Linux

​ 

目录

1. 搜索二叉树

1.1 概念

1.2 搜索二叉树操作

2. 模拟实现搜索二叉树 

2.1 非递归版本

2.1.1 基本构造

2.1.2 插入

2.1.3 删除

2.1.4 查找

2.2 递归版本

2.2.1 插入

2.2.2 删除

2.2.3 查找

2.2.4 中序遍历

3. 完整代码


1. 搜索二叉树

1.1 概念

二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:

  • 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  • 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  • 它的左右子树也分别为二叉搜索树

1.2 搜索二叉树操作

 

1. 二叉树的查找

a、从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找。
b、最多查找高度次,走到空,还没找到,这个值不存在。

2. 二叉树的插入

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

3. 二叉树的删除 

首先查找元素是否在二叉搜索树中,如果不存在,则返回, 否则要删除的结点可能分下面四种情况:
a. 要删除的结点无孩子结点
b. 要删除的结点只有左孩子结点
c. 要删除的结点只有右孩子结点
d. 要删除的结点有左、右孩子结点

2. 模拟实现搜索二叉树 

搜索二叉树有两种模型:

1. Key模型:节点中只存在一个值key,并且这个值不可以修改,比如后面学习到的set

2. Key_Value模型:节点中存在两个值,一个是key,不可修改,另一个是与key对应的value,可以修改,比如后面学习到的map

在这里我们只实现Key模型的搜索二叉树

2.1 非递归版本

2.1.1 基本构造
//节点
template<class K>
struct BSTreeNode
{BSTreeNode* _left;BSTreeNode* _right;K _key;//构造BSTreeNode(const K& key):_left(nullptr),_right(nullptr),_key(key){}
};//搜索二叉树
template<class K>
class BSTree
{
public:typedef BSTreeNode<K> Node;//构造BSTree() //给定了缺省值{}//拷贝构造BSTree(const BSTree<K>& tmp){_root = Copy(tmp._root);}//operator=BSTree<K> operator=(BSTree<K> tmp){swap(_root, tmp._root);return *this;}//析构~BSTree(){Destroy(_root);}
private://递归拷贝左右子树Node* Copy(Node* root){if (root == nullptr)return nullptr;Node* newNode = new Node(root->_key);newNode->_left = Copy(root->_left);newNode->_right = Copy(root->_right);return newNode;}//后序遍历删除void Destroy(Node*& root){if (root == nullptr){return;}Destroy(root->_left);Destroy(root->_right);delete root;root = nullptr;}private:Node* _root = nullptr;  //缺省值给空即可
}; 
2.1.2 插入

插入时如果为空直接插入即可,若存在节点,需要先进行判断,比根节点大的插入到它的右子树,比根节点小的插入左子树即可,这时需要注意的插入的节点需要与它的父节点进行链接,这时在往下比较的过程中就需要记录一下它的父节点。

//插入bool Insert(const K& key){//如果为空可以直接插入链接if (_root == nullptr){_root = new Node(key);return true;}//记录父节点Node* parent = nullptr;Node* cur = _root;//遍历找到合适的节点进行插入链接while (cur){parent = cur;if (cur->_key < key){cur = cur->_right;}else if (cur->_key > key){cur = cur->_left;}elsereturn false;}//链接cur = new Node(key);if (parent->_key < key){parent->_right = cur;}else{parent->_left = cur;}return true;}
2.1.3 删除

首先找到需要删除的节点,删除的时候需要注意分下面几种情况:

  • 左子树为空,可以直接删除
  • 右子树为空,可以直接删除
  • 左右子树都不为空需要使用替换法删除
  • 替换法:使用左子树最大的节点替换需要删除的节点,或者使用右子树最小的节点替换需要删除的节点,替换之后直接删除被替换的节点即可完成删除。
  • 需要注意的是在这个过程中需要记录父节点,在删除之后需要及时链接,并且要注意的是删除的节点是根节点的时候可以直接将它的左右子树直接链接。
//删除bool Erase(const K& key){Node* cur = _root;//记录父亲Node* parent = nullptr;while (cur){//找到要删除的keyif (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->_left = cur->_right;}else if (cur == parent->_right){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* parent = cur;//右树的最小节点进行替换或者左树的最大节点Node* subRight = cur->_right;while (subRight->_left)  //找到右树的最小节点{parent = subRight;subRight = subRight->_left;}swap(cur->_key, subRight->_key);  //替换两个节点//将删除节点的右树链接在它的父亲if (parent->_left == subRight){parent->_left = subRight->_right;}else {parent->_right = subRight->_right;}delete subRight;}return true;}}return false;}
2.1.4 查找
//查找bool Find(const K& key){Node* cur = _root;while (cur){if (cur->_key > key){cur = cur->_left;}else if (cur->_key < key){cur = cur->_right;}elsereturn true;}return false;}

2.2 递归版本

2.2.1 插入

递归插入时也需要进行一层封装,在里面传递root,在这里采用引用传参比较好,可以不用额外的链接,遇到空直接创建一个节点即可,直接在原数上进行操作。

//插入bool InsertR(const K& key){return _InsertR(key, _root);}bool _InsertR(const K& key, Node*& root){//树为空直接插入即可if (root == nullptr){root = new Node(key);return true;}//递归左if (root->_key > key)return _InsertR(key, root->_left);else if (root->_key < key)  //递归右return _InsertR(key, root->_right);elsereturn false;}
2.2.2 删除

还是采用里面封装一层,在递归删除的时候先递归找到要删除的key,然后判断它的左右子树,如果左右子树只存在一个可以直接进行删除,然后将它的孩子链接在它的节点上,如果左右孩子均存在,使用替换法,用该节点的右子树的最小节点进行替换,先使用循环找到该最小节点,然后与其交换,然后转化为递归该节点右子树的删除问题即可。

//删除bool EraseR(const K& key){return _EraseR(key, _root);}
bool _EraseR(const K& key, Node*& root){if (root == nullptr)return false;//查找keyif (root->_key < key){return _EraseR(root->_right, key);}else if (root->_key > 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* subRight = root;  //找到右树的最小节点while (subRight->left){subRight = subRight->_left;}swap(root->_key, subRight->_key);  //交换return _EraseR(key, root->_right);   //转化为递归右子树的子问题}}}
2.2.3 查找
//查找bool FindR(const K& key){_FindR(key, _root);}
bool _FindR(const K& key, Node* root){if (root == nullptr)return false;if (root->_key > key)return _FindR(root->_left);else if (root->_key < key)return _FindR(root->_right);elsereturn true;}
2.2.4 中序遍历

中序遍历时需要封装一层,在外面不好传递节点,中序遍历:左子树、根、右子树

	//中序遍历void InOrder(){_InOrder(_root);cout << endl;}void _InOrder(Node* root){if (root == nullptr)return; _InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);}

3. 完整代码

#pragma once
#include <iostream>using namespace std;template<class K>
struct BSTreeNode
{BSTreeNode* _left;BSTreeNode* _right;K _key;BSTreeNode(const K& key):_left(nullptr),_right(nullptr),_key(key){}
};template<class K>
class BSTree
{
public:typedef BSTreeNode<K> Node;//构造BSTree() //给定了缺省值{}//拷贝构造BSTree(const BSTree<K>& tmp){_root = Copy(tmp._root);}//operator=BSTree<K> operator=(BSTree<K> tmp){swap(_root, tmp._root);return *this;}//析构~BSTree(){Destroy(_root);}//非递归版本//插入bool Insert(const K& key){//如果为空可以直接插入链接if (_root == nullptr){_root = new Node(key);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){parent = cur;if (cur->_key < key){cur = cur->_right;}else if (cur->_key > key){cur = cur->_left;}elsereturn false;}//链接cur = new Node(key);if (parent->_key < key){parent->_right = cur;}else{parent->_left = cur;}return true;}//删除bool Erase(const K& key){Node* cur = _root;//记录父亲Node* parent = nullptr;while (cur){//找到要删除的keyif (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->_left = cur->_right;}else if (cur == parent->_right){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* parent = cur;//右树的最小节点进行替换或者左树的最大节点Node* subRight = cur->_right;while (subRight->_left)  //找到右树的最小节点{parent = subRight;subRight = subRight->_left;}swap(cur->_key, subRight->_key);  //替换两个节点//将删除节点的右树链接在它的父亲if (parent->_left == subRight){parent->_left = subRight->_right;}else{parent->_right = subRight->_right;}delete subRight;}return true;}}return false;}//查找bool Find(const K& key){Node* cur = _root;while (cur){if (cur->_key > key){cur = cur->_left;}else if (cur->_key < key){cur = cur->_right;}elsereturn true;}return false;}//递归版本//插入bool InsertR(const K& key){return _InsertR(key, _root);}//删除bool EraseR(const K& key){return _EraseR(key, _root);}//查找bool FindR(const K& key){_FindR(key, _root);}//中序遍历void InOrder(){_InOrder(_root);cout << endl;}private://插入bool _InsertR(const K& key, Node*& root){//树为空直接插入即可if (root == nullptr){root = new Node(key);return true;}//递归左if (root->_key > key)return _InsertR(key, root->_left);else if (root->_key < key)  //递归右return _InsertR(key, root->_right);elsereturn false;}//删除bool _EraseR(const K& key, Node*& root){if (root == nullptr)return false;//查找keyif (root->_key < key){return _EraseR(root->_right, key);}else if (root->_key > 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* subRight = root;  //找到右树的最小节点while (subRight->left){subRight = subRight->_left;}swap(root->_key, subRight->_key);  //交换return _EraseR(key, root->_right);   //转化为递归右子树的子问题}}}//查找bool _FindR(const K& key, Node* root){if (root == nullptr)return false;if (root->_key > key)return _FindR(root->_left);else if (root->_key < key)return _FindR(root->_right);elsereturn true;}//中序遍历void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);}//拷贝Node* Copy(Node* root){if (root == nullptr)return nullptr;Node* newNode = new Node(root->_key);newNode->_left = Copy(root->_left);newNode->_right = Copy(root->_right);return newNode;}//销毁void Destroy(Node*& root){if (root == nullptr){return;}Destroy(root->_left);Destroy(root->_right);delete root;root = nullptr;}
private:Node* _root = nullptr;
};

朋友们、伙计们,美好的时光总是短暂的,我们本期的的分享就到此结束,欲知后事如何,请听下回分解~,最后看完别忘了留下你们弥足珍贵的三连喔,感谢大家的支持!     

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

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

相关文章

C语言之动态内存管理(malloc calloc realloc)

C语言之动态内存管理 文章目录 C语言之动态内存管理1. 为什么要有动态内存管理2. malloc 和 free2.1 malloc2.2 free2.3 例子 3. calloc 和 realloc3.1 calloc3.2 realloc 4. 常见的动态内存错误4.1 对NULL指针的解引⽤操作4.2 对动态开辟空间的越界访问4.3 对⾮动态开辟内存使…

女装品牌网站建设的作用如何

服装是任何人都需要的必备品&#xff0c;尤其是女装&#xff0c;由于女性群体爱美追求时尚的心理更高&#xff0c;因此市场中有大量女装品牌以及大量消费者&#xff0c;其规模非常高&#xff0c;众多大小品牌林立及消费征集下&#xff0c;商家们经营也并不太容易&#xff0c;企…

Themis: Fast, Strong Order-Fairness in Byzantine Consensus

目录 笔记后续的研究方向摘要引言秩序井然 Themis: Fast, Strong Order-Fairness in Byzantine Consensus CCS 2023 笔记 后续的研究方向 摘要 我们介绍了Themis&#xff0c;这是一种将交易的公平排序引入&#xff08;许可的&#xff09;拜占庭共识协议的方案&#xff0c;最…

参加百度Apollo技术沙龙—感受自动驾驶的魅力

2023年12月2日下午2点&#xff0c;我有幸参加了百度Apollo技术沙龙&#xff0c;这是一个围绕Apollo新版本Beta的全面升级展开的深度交流活动。作为一名工程师&#xff0c;我深感荣幸能够与众多同行和专家一同探讨自动驾驶技术的快速发展 在这次沙龙中&#xff0c;我了解到Apo…

vue的data

类型&#xff1a;Object | Function 限制&#xff1a;组件的定义只接受 function。 详细&#xff1a; Vue 实例的数据对象。Vue 会递归地把 data 的 property 转换为 getter/setter&#xff0c;从而让 data 的 property 能够响应数据变化。对象必须是纯粹的对象 (含有零个或多个…

DC电源模块与节能环保的关系

BOSHIDA DC电源模块与节能环保的关系 随着全球能源危机的加剧&#xff0c;环保节能已经成为世界各国政府和企业发展的主要方向。在电子行业中&#xff0c; DC电源模块的出现为环保节能做出了贡献。DC电源模块是一种电源供应器件&#xff0c;可将高电压转换为低电压&#xff0c;…

柏林噪声C++

柏林噪声 随机噪声 如上图所示随机噪声没有任何规律可言&#xff0c;我们希望生成有一些意义的局部连续的随机图案 一维柏林噪声 假设希望生成一段局部连续的随机曲线&#xff0c;可以采用插值的方式&#xff1a;在固定点随机分配y值&#xff08;一般是整数点&#xff09;&a…

【数据分析实战】酒店行业华住集团门店分布与评分多维度分析

文章目录 1. 写在前面2. 数据集展示3. 多维度分析3.1 门店档次多元化&#xff1a;集团投资战略观察3.1.1 代码实现3.1.2 本人浅薄理解 3.2 门店分布&#xff1a;各省市分布概览3.2.1 代码实现3.2.2 本人浅薄理解 3.3 门店分级评分&#xff1a;服务水平的多维度观察3.3.1 代码实…

F5怎么样?从负载均衡到云原生的进阶之路

从Web时代开始至云原生时代的应用服务交付的市场&#xff0c;技术与人的变化就是关注的焦点。从单纯的Web负载均衡到复杂的企业应用交付&#xff0c;从单体应用到分布式、微服务架构&#xff0c;F5为企业技术架构更好、更优、更安全的运行做出了极大的努力。那么F5怎么样&#…

题目:分糖果(蓝桥OJ 2928)

题目描述&#xff1a; 解题思路&#xff1a; 本题采用贪心思想 图解 题解&#xff1a; #include<bits/stdc.h> using namespace std;const int N 1e6 9; char s[N];//写字符串数组的一种方法,像数组一样***int main() {int n, x;cin >> n >> x;for(int …

CSS新手入门笔记整理:元素类型相互转换

元素类型 块元素&#xff08;block&#xff09; 独占一行&#xff0c;排斥其他元素跟其位于同一行&#xff0c;包括块元素和行内元素。块元素内部可以容纳其他块元素和行内元素。可以定义 width&#xff0c;也可以定义 height。可以定义 4 个方向的 margin。 行内元素&#xf…

使用navicat(或者其他数据库管理工具)、powerdesigner导出数据字典

适合先有数据库结构&#xff0c;后需要导出数据字典的情况&#xff0c;多数在发开完成交文档或者用户有库的情况下 有条件的话推荐用powerdesigner导出&#xff0c;比较好看 如果用powerdesigner导出的注释不对&#xff0c;是因为数据库的编码不对 1、使用navicat导出 在该数…

代码随想录算法训练营第45天| 70. 爬楼梯 (进阶) 322. 零钱兑换 279.完全平方数

JAVA代码编写 70. 爬楼梯&#xff08;进阶版) 卡码网&#xff1a;57. 爬楼梯&#xff08;第八期模拟笔试&#xff09; 题目描述 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬至多m (1 < m < n)个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f…

菜鸟学习日记(python)——推导式

python中的推导式是一种独特的数据处理方式&#xff0c;可以从一个数据序列去构建另一个新的数据序列的结构体。 它包括以下推导式&#xff1a; 列表&#xff08;list&#xff09;推导式字典&#xff08;dict&#xff09;推导式集合&#xff08;set&#xff09;推导式元组&am…

Multi-Cell Downlink Beamforming: Direct FP, Closed-Form FP, Weighted MMSE

这里写自定义目录标题 Direct FPClosed-Form FPthe Lagrangian functionthe Lagrange dual function: maximizing the Lagrangianthe Lagrange dual problem: minimizing the Lagrange dual functionClosed-Form FP Weighted MMSE原论文 Lagrange dual5.1.1 The Lagrangian5.1.…

阿里云服务器经济型、通用算力型、计算型、通用型、内存型实例区别及选择参考

当我们通过阿里云的活动购买云服务器会发现&#xff0c;相同配置的云服务器往往有多个不同的实例可选&#xff0c;而且价格差别也比较大&#xff0c;例如同样是4核8G的配置的云服务器&#xff0c;经济型e实例活动价格只要1500.48/1年起&#xff0c;通用算力型u1实例要1795.97/1…

nvidia安装出现7-zip crc error解决办法

解决办法&#xff1a;下载network版本&#xff0c;重新安装。&#xff08;选择自己需要的版本&#xff09; 网址&#xff1a;CUDA Toolkit 12.3 Update 1 Downloads | NVIDIA Developer 分析原因&#xff1a;local版本的安装包可能在下载过程中出现损坏。 本人尝试过全网说的…

无公网IP环境如何SSH远程连接Deepin操作系统

文章目录 前言1. 开启SSH服务2. Deppin安装Cpolar3. 配置ssh公网地址4. 公网远程SSH连接5. 固定连接SSH公网地址6. SSH固定地址连接测试 前言 Deepin操作系统是一个基于Debian的Linux操作系统&#xff0c;专注于使用者对日常办公、学习、生活和娱乐的操作体验的极致&#xff0…

Python---time库

目录 时间获取 时间格式化 程序计时 time库包含三类函数&#xff1a; 时间获取&#xff1a;time() ctime() gmtime() 时间格式化&#xff1a;strtime() strptime() 程序计时&#xff1a;sleep() perf_counter() 下面逐一介绍&#…

H3.3K27M弥漫性中线胶质瘤的反义寡核苷酸治疗

今天给同学们分享一篇实验文章“Antisense oligonucleotide therapy for H3.3K27M diffuse midline glioma”&#xff0c;这篇文章发表在Sci Transl Med期刊上&#xff0c;影响因子为17.1。 结果解读&#xff1a; CRISPR-Cas9消耗H3.3K27M恢复了H3K27三甲基化&#xff0c;并延…