【C++】二叉搜索数

目录

一、二叉搜索树的概念

二、二叉搜索树的模拟实现

1、定义节点

2、构造二叉树

3、析构二叉树

​4、拷贝二叉树

5、二叉树赋值

6、插入节点

🌟【非递归方式】

🌟【递归方式】

7、打印节点

 8、搜索节点

🌟【非递归方式】

🌟【递归方式】

9、删除节点(重要)

🌟【非递归方式】

🌟【递归方式】

10、完整代码

三、二叉搜索树的应用

K模型&&KV模型


一、二叉搜索树的概念

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

二、二叉搜索树的模拟实现

1、定义节点

首先需要确定二叉树的节点,有左节点、和右节点以及节点的值。

2、构造二叉树

3、析构二叉树

遍历左右二叉树,删除节点并将节点置为空。

 4、拷贝二叉树

拷贝二叉树,我们不能使用insert来一个一个插入,因为inset带有筛选的功能,最后结果顺序会不同的。
我们使用类似于前序遍历的方式,进行拷贝,遍历到哪就相当于拷贝到哪。
首先遇到空就返回。1.拷贝结点 2.递归拷贝左子树3.递归拷贝右子树。4.最后将拷贝结点返回。

5、二叉树赋值

6、插入节点

🌟【非递归方式】

过程:

a. 树为空,则直接新增节点,赋值给root指针

b. 树不空,按二叉搜索树性质查找插入位置,插入新节点 

步骤:

a.当插入的结点值key要比根结点大,则key需要到根的右树进行比较,当key的值比根结点小,则key需要到根的左树进行比较,当key的值与根结点相同时,则返回fasle,按照这样的方式循环下去,当要比较的结点为空时,则就可以将结点插入到这个位置上了。每次比较中都要记录父节点的位置,因为最后需要链接起来。
b.最后链接起来需要和父结点比较一下才能知道链接到父节点的左边还是右边。如果大于父节点则链接到右边,如果小于父节点则连接到左边。

 

🌟【递归方式】

递归实现方式的思想其实是一样的,如果节点为空,就创造一个节点。如果不为空,就比较节点与需要插入值的大小,如果比节点大就插入右边,如果比节点小就插入左边,遇到空就新建一个节点。当递归结束时,就可以将开辟好结点链接起来。(递归的过程就是不断的在创建结点,回来的过程就是不断地将结点链接起来)。这里不需要像非递归那样,每次比较都需要记录父节点的位置,我们这里用一个引用就可以轻松解决问题!我们的指针参数使用引用,即子函数的参数是递归函数参数的别名。

 

7、打印节点

运用递归,分别打印 root 左边和右边的数值,遇到空就返回。

 

 8、搜索节点

过程:

a.从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找。如果没有找到就返回 false.

b.最多查找高度次,走到到空,还没找到,这个值不存在。

🌟【非递归方式】

🌟【递归方式】

当所要找的节点小于根节点时,递归到左子树去找;当所要找的节点大于根节点时,递归到右节点去找。当等于所要找的节点时,返回真。如果一直找不到则返回假。

 

9、删除节点(重要)

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

a. 要删除的结点无孩子结点

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

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

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

看起来有待删除节点有4中情况,实际情况a可以与情况b或者c合并起来,因此真正的删除过程如下:

a.当删除结点没有孩子节点时,使用托孤法,将父节点与空链接起来。--直接删除
b.当删除结点只有一个孩子节点时,使用托孤法,将父节点与孤结点链接起来。--直接删除
c.当删除结点有两个孩子节点时,使用找保姆法,找一个可以替代本身的结点。交换这两个结点,删除这个替换结点。一般来说是将root节点和左子树的最大结点或者是右子树的最小结点相交换。--替换法删除

不管怎么删除都要遵循先查找后删除的原则,当要删除的数字比root大,就往右边查找;当要删除的数字比root小就往左边查找。 

🌟【非递归方式】

 1)当只有右孩子节点时,左孩子节点为空时

首先要判断父节点是不是 root,然后判断要删除的节点时父节点的左孩子节点还是右孩子节点,如果是左孩子节点就将删除节点的孩子节点链接上父节点的左边;如果是右孩子节点就将删除节点的孩子节点链接上父节点的右边。

 2)当只有左孩子节点时,右孩子节点为空时

 3)当存在两个孩子节点时

如果我们直接删除,那可能需要改变树的结构,这样会很复杂。我们可以找到需要删除节点的左边最大值,右边最小值相交换,然后再删除。

 【测试运行】

🌟【递归方式】

当key比根结点大的时候,递归到右子树进行比较,当key比根结点值小的时候,递归到左子树进行比较,当key跟结点值相同时,表明找到了。找到后就要分三种情况讨论。

【当存在一个孩子结点时】
不同于非递归版本需要记录父节点,递归版本不需要记录父节点,因为一个引用,让我们省去了很多麻烦。root 就是父节点的左指针或者右指针。托孤直接托孤给root即可。因为root就是父节点的左右指针指向。
当右子树不存在时,直接将要删除结点的左子树托孤给root。当左子树不存在时,直接将要删除结点的右子树托孤给root。

【当存在两个孩子结点时】
当存在两个孩子结点时,还是需要使用保姆法,首先找到保姆结点,然后将要删除结点的值与保姆结点值交换。这样就转化成子问题了。从整棵树来看,因为保姆结点和删除结点交换,而改变了搜索二叉树的特性。不能使用了。
但从左子树来看,还是完整的二叉搜索树,并且要删除结点就只有一个孩子,直接转化为上面的问题。

10、完整代码

#define  _CRT_SECURE_NO_WARNINGS
#pragma oncetemplate<class K>
struct BSTreeNode
{BSTreeNode<K>* _left;BSTreeNode<K>* _right;K _key;//初始化BSTreeNode(const K& key):_left(nullptr), _right(nullptr), _key(key){}
};template<class K>
class BSTree
{typedef BSTreeNode<K> Node;
public:/*BSTree():_root(nullptr){}*/BSTree() = default; // 制定强制生成默认构造BSTree(const BSTree<K>& t){_root = Copy(t._root);}BSTree<K>& operator=(BSTree<K> t){swap(_root, t._root);return *this;}//~BSTree()//{//	Destroy(_root);//	//_root = nullptr;//}//非递归方式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;}//递归方式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 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;}}return false;}//递归方式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 true;if (root->_key < key)return _FindR(root->_right, key);elsereturn _FindR(root->_left, key);}void InOrder(){_InOrder(_root);cout << endl;}void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);}//非递归方式bool Erase(const K& key){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{// 删除// 1、左为空if (cur->_left == nullptr){//如果cur是根节点,直接删除根节点,更改root值if (cur == _root){_root = cur->_right;}else{//判断cur 是父节点的左孩子还是右孩子if (parent->_left == cur){parent->_left = cur->_right;}else{parent->_right = cur->_right;}}delete cur;} // 2、右为空else if (cur->_right == nullptr){if (cur == _root){_root = cur->_left;}else{if (parent->_left == cur){parent->_left = cur->_left;}else{parent->_right = cur->_left;}}delete cur;}else{// 找右树最小节点替代,也可以是左树最大节点替代Node* pminRight = cur;Node* minRight = cur->_right;while (minRight->_left){pminRight = minRight;minRight = minRight->_left;}cur->_key = minRight->_key;if (pminRight->_left == minRight){pminRight->_left = minRight->_right;}else{pminRight->_right = minRight->_right;}delete minRight;}return true;}}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* maxleft = root->_left;while (maxleft->_right){maxleft = maxleft->_right;}swap(root->_key, maxleft->_key);return _EraseR(root->_left, key);}delete del;return true;}}~BSTree(){Destroy(_root);//_root = nullptr;}void Destroy(Node*& root){if (root == nullptr)return;Destroy(root->_left);Destroy(root->_right);delete root;root = nullptr;}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);return newRoot;}BSTree<K>& operator=(BSTree<K> t){swap(_root, t._root);return *this;}
private:Node* _root = nullptr;
};

三、二叉搜索树的应用

K模型&&KV模型

K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到的值。

比如:给一个单词word,判断该单词是否拼写正确,具体方式如下: 以词库中所有单词集合中的每个单词作为key,构建一棵二叉搜索树 在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。

KV模型:每一个关键码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:bool Insert(const K& key, const V& value){if (_root == nullptr){_root = new Node(key, value);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, value);// 链接if (parent->_key < key){parent->_right = cur;}else{parent->_left = cur;}return true;}Node* 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 cur;}}return nullptr;}bool Erase(const K& key){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{// 删除// 1、左为空if (cur->_left == nullptr){if (cur == _root){_root = cur->_right;}else{if (parent->_left == cur){parent->_left = cur->_right;}else{parent->_right = cur->_right;}}delete cur;} // 2、右为空else if (cur->_right == nullptr){if (cur == _root){_root = cur->_left;}else{if (parent->_left == cur){parent->_left = cur->_left;}else{parent->_right = cur->_left;}}delete cur;}else{// 找右树最小节点替代,也可以是左树最大节点替代Node* pminRight = cur;Node* minRight = cur->_right;while (minRight->_left){pminRight = minRight;minRight = minRight->_left;}cur->_key = minRight->_key;if (pminRight->_left == minRight){pminRight->_left = minRight->_right;}else{pminRight->_right = minRight->_right;}delete minRight;}return true;}}return false;}void InOrder(){_InOrder(_root);cout << endl;}protected:void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_key << ":" << root->_value << endl;_InOrder(root->_right);}private:Node* _root = nullptr;};

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

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

相关文章

我的需求分析方法论

或网上看了无数博客文章、技术视频&#xff0c;或购买金装版本技术书籍&#xff0c;看过无数原理原则、各种各样经典方法论&#xff0c;真正在实际开发工作中&#xff0c;本能去遵守和执行的又留下多少呢。 启动一个新系统时&#xff0c;我们可能还会去花些时间遵循这些原理原则…

中文大模型隐私保护哪家强?InternLM 与 Baichuan2 胜出!

引言&#xff1a;中文大模型隐私保护能力探索 本文研究了大语言模型&#xff08;LLMs&#xff09;对隐私和安全的影响&#xff0c;采用了三层渐进框架对语言系统的隐私进行评估。主要目标是全面评估LLMs对私人信息的敏感性&#xff0c;并检查其在识别、管理和保护敏感数据方面…

每日面经分享(Git经典题目,Git入门)

1. GitHub是什么 a. Git是一个分布式版本控制系统&#xff0c;作用是跟踪、管理和协调软件开发项目中的代码更改。 b. 提供了一种有效的方式来管理代码的版本历史&#xff0c;以及多人协作开发的能力。 2. Git的作用有哪些 a. 版本控制&#xff1a;Git可以记录每次代码更改的…

混合专家(MoE)模型

文心一言 混合专家模型&#xff08;Mixture of Experts&#xff0c;简称MoE&#xff09;是一种基于Transformer架构的模型设计策略。它通过将多个模型&#xff08;称为“专家”&#xff09;直接结合在一起&#xff0c;以获得更好的预测性能。这种模型特别适用于处理大规模数据…

1.8 面试经典150题 O(1)时间插入删除和获取随机元素

O(1)时间插入删除和获取随机元素 实现RandomizedSet 类&#xff1a; RandomizedSet() 初始化 RandomizedSet 对象bool insert(int val) 当元素 val 不存在时&#xff0c;向集合中插入该项&#xff0c;并返回 true &#xff1b;否则&#xff0c;返回 false 。bool remove(int va…

vLLM vs TGI 部署大模型以及注意点

LLM 高并发部署是个难题&#xff0c;具备高吞吐量的服务&#xff0c;能够让用户有更好的体验&#xff08;比如模型生成文字速度提升&#xff0c;用户排队时间缩短&#xff09;。 vllm github 仓库 1 vLLM 1.1 启动模型服务 # cd /workspace/vllm python3 -m vllm.entrypoin…

springAI初体验 让人人都能跑大模型

springAI初体验 让人人都能跑大模型 Spring AI是一个旨在简化开发包含人工智能功能的应用程序的项目。它受到Python项目如LangChain和Llama Index的启发&#xff0c;但并非这些项目的直接移植。Spring AI的核心理念是为开发AI应用程序提供基础抽象&#xff0c;这些抽象有多个实…

leetcode496-Next Greater Element I

题目&#xff1a; nums1 中数字 x 的 下一个更大元素 是指 x 在 nums2 中对应位置 右侧 的 第一个 比 x 大的元素。 给你两个 没有重复元素 的数组 nums1 和 nums2 &#xff0c;下标从 0 开始计数&#xff0c;其中nums1 是 nums2 的子集。 对于每个 0 < i < nums1.lengt…

MyBatis中的#{}与${}注入问题

MyBatis中的#{}与${}注入问题 MyBatis是一个流行的Java持久层框架,用于将对象与数据库中的数据进行映射。然而,如果不当使用,MyBatis也可能受到诸如SQL注入这类的安全问题的影响。 SQL注入是一种攻击技术,攻击者通过在输入中插入恶意SQL语句片段,企图对数据库执行非预期的…

Everything搭建http服务器

突然发现everything还可以搭建http服务器&#xff0c;给大家分享一下 打开everything&#xff0c;按图示填写或选择内容 在浏览器输入你的本地地址和端口 再输入刚才设置的账户密码&#xff0c;即可使用

接口测试常用代理工具

些代理工具可以帮助我们构造各种测试场景、以及更好的完成测试工作。下面的介绍以 Charles 为主。 Charles Charles 是一款代理服务器&#xff0c;可以截取请求和响应达到分析抓包的目的&#xff0c;且支持多平台&#xff0c;能够在 Windows&#xff0c;Mac&#xff0c;Linux…

【OJ】动规练习七之【模板】01背包

个人主页 &#xff1a; zxctscl 如有转载请先通知 DP41 【模板】01背包 1. DP41 【模板】01背包2. 分析3. 代码4. 优化5. 优化后代码 1. DP41 【模板】01背包 2. 分析 一、题目解析&#xff1a; 来看一下例1&#xff0c;3代表有三个物品&#xff0c;5代表能够容纳的体积。第一…

1970-2021年全国区县级碳排放数据8

1970-2021年全国区县级碳排放数据 1、时间&#xff1a;1970-2021年 2、指标&#xff1a;2877个区县 3、来源&#xff1a;EDGAR 4、指标&#xff1a;二氧化碳排放量 5、样本量&#xff1a;14W 6、指标解释&#xff1a; 二氧化碳排放是一个生态环境专业术语&#xff0c;主…

【Python系列】读取 Excel 第一列数据并赋值到指定列

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

PyCharm远程链接AutoDL

AutoDL使用方法&#xff1a; Step1&#xff1a;确认您安装的PyCharm是社区版还是专业版&#xff0c;只有专业版才支持远程开发功能。 Step2&#xff1a;开机实例 复制自己实例的SSH指令&#xff0c;比如&#xff1a;ssh -p 38076 rootregion-1.autodl.com 在ssh -p 38076 roo…

二、计算机网络体系结构参考模型

一、分层结构 &#xff08;一&#xff09;为什么要分层&#xff1a; 发送文件/数据前要完成的工作&#xff1a; 1&#xff09;发起通信的计算机必须讲数据通信通路进行激活 2&#xff09;要告诉网络如何识别目的主机 3&#xff09;发起通信的计算机要查明目的主机是否开机、并且…

先登杯·14天创作挑战营·第④期~ 等你来战!

文章目录 ⭐️ 活动介绍⭐️ 活动详情⭐️ 活动奖品⭐️ 活动流程​⭐️ 评审规则⭐️ 报名&投稿注意事项⭐️ 活动组织 ​ 活动报名入口&#xff1a;https://bbs.csdn.net/topics/618374514 本次活动与官方活动及其他博主的创作型活动并不冲突&#xff01; ​ ​ ⭐️…

android studio中添加module依赖

android常用的三种依赖 库依赖&#xff08;Library dependency&#xff09;&#xff1a;以访问网址的形式将依赖库相应版本下载到本地; 文件依赖&#xff08;File dependency&#xff09;&#xff1a; 将下载下来的依赖库以.jar文件的形式添加依赖. module依赖&#xff08;Modu…

【力扣】168. Excel表列名称、171. Excel 表列序号

168. Excel表列名称 题目描述 给你一个整数 columnNumber &#xff0c;返回它在 Excel 表中相对应的列名称。 例如&#xff1a; A -> 1 B -> 2 C -> 3 … Z -> 26 AA -> 27 AB -> 28 … 示例 1&#xff1a; 输入&#xff1a;columnNumber 1 输出&#xf…

Go语言hash/fnv应用实战:技巧、示例与最佳实践

Go语言hash/fnv应用实战&#xff1a;技巧、示例与最佳实践 引言hash/fnv概览使用hash/fnv的初步步骤导入hash/fnv库创建哈希器实例 hash/fnv在实际开发中的应用生成唯一标识符数据分片与负载均衡快速查找 高级技巧和最佳实践避免哈希碰撞动态调整哈希表大小利用sync.Pool优化哈…