二叉树进阶

目录

1. 二叉搜索树实现

1.1 二叉搜索树概念

2.2 二叉搜索树操作

​编辑

​编辑

2.3 二叉搜索树的实现

2.3.0  Destroy()   + 析构

2.3.1 Insert()插入

2.3.2 InOrder() 打印搜索二叉树

​编辑​编辑

2.3.3 Find() 查找

2.3.4  Erase()  删除

​编辑

​编辑

​编辑

2.3.5 完整代码

3. ⼆叉搜索树key和key/value使⽤场景

3.1 key搜索场景:

 3.2 key/value搜索场景:


1. 二叉搜索树实现

1.1 二叉搜索树概念


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

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

2.2 二叉搜索树操作

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

1. 二叉搜索树的查找

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

2. 二叉搜索树的插入
插入的具体过程如下:

  • a. 树为空,则直接新增节点,赋值给root指针
  • b. 树不空,按二叉搜索树性质查找插入位置,插入新节点

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

  • a. 要删除的结点无孩子结点
  • b. 要删除的结点只有左孩子结点
  • c. 要删除的结点只有右孩子结点
  • d. 要删除的结点有左、右孩子结点

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

  • 情况b:删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点--直接删除
  • 情况c:删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点--直接删除
  • 情况d:在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点
  • 中,再来处理该结点的删除问题--替换法删除

2.3 二叉搜索树的实现

#pragma once
template<class K>
struct BSTNode
{K _key;BSTNode<K>* _left;BSTNode<K>* _right;BSTNode(const K& key):_key(key), _left(nullptr), _right(nullptr){}};
template<class K>
class BSTree
{using Node = BSTNode<K>;//等价于 typedef BsTNode<K>;public:bool Insert(const K& key){}
private:Node* _root = nullptr;
};

2.3.0  Destroy()   + 析构

	void Destroy(Node* root){if (root == nullptr)return;Destroy(root->_left);Destroy(root->_right);delete root;}
	~BSTree(){Destroy(_root);_root = nullptr;}

2.3.1 Insert()插入

插⼊的具体过程如下:

  • 1. 树为空,则直接新增结点,赋值给root指针
  • 2. 树不空,按⼆叉搜索树性质,插入值比当前结点大往右走,插入值比当前结点小往左走,找到空位
  • 置,插入新结点。
  • 3. 如果支持插⼊相等的值,插入值跟当前结点相等的值可以往右走,也可以往左走,找到空位置,插入新结点。(要注意的是要保持逻辑⼀致性,插入相等的值不要一会往右走,一会往左⾛)

bool Insert(const K& key)
{if (_root == nullptr){_root = new Node(key);}Node* parent = nullptr;Node* cur = _root;//用parent找到满足搜索二叉树的节点进行插入//cur负责查找位置//像这种的逻辑上连续,物理上不连续的链式结构一般都用双指针解决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;
}

2.3.2 InOrder() 打印搜索二叉树

	//提供Get()函数,访问内部私有函数void InOrder(){_InOrder( _root);}
private:void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);}

测试用例 

#include"FileName.h"
int main()
{BSTree<int> t;int a[] = { 8,3,1,10,1,6,4,7,14,13 };for (auto e : a){t.Insert(e);}t.InOrder();return 0;
}

2.3.3 Find() 查找

  • 1. 从根开始比较,查找x,x比根的值大则往右边走查找,x比根值小则往左边走查找。
  • 2. 最多查找高度次,走到到空,还没找到,这个值不存在。
  • 3. 如果不支持插入相等的值,找到x即可返回
  • 4. 如果支持插入相等的值,意味着有多个x存在,一般要求查找中序的第一个x。如下图,查找3,要找到1的右孩子的那个3返回

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;
}

2.3.4  Erase()  删除


首先查找元素是否在二叉搜索树中,如果不存在,则返回false。
如果查找元素存在则分以下四种情况分别处理:(假设要删除的结点为N)

  • 1. 要删除结点N左右孩子均为空
  • 2. 要删除的结点N左孩子位空,右孩子结点不为空
  • 3. 要删除的结点N右孩子位空,左孩子结点不为空
  • 4. 要删除的结点N左右孩子结点均不为空

对应以上四种情况的解决方案:
1. 把N结点的父亲对应孩子指针指向空,直接删除N结点(情况1可以当成2或者3处理,效果是一样的)


2. 把N结点的父亲对应孩子指针指向N的右孩子,直接删除N结点
3. 把N结点的父亲对应孩子指针指向N的左孩子,直接删除N结点
4. 无法直接删除N结点,因为N的两个孩子无处安放,只能用替换法删除。找N左子树的值最大结点
R(最右结点)或者N右子树的值最小结点R(最左结点)替代N,因为这两个结点中任意⼀个,放到N的
位置,都满足二叉搜索树的规则。替代N的意思就是N和R的两个结点的值交换,转而变成删除R结
点,R结点符合情况2或情况3,可以直接删除。

bool Erase(const K& key)
{Node* parent = _root;Node* cur = _root;while (cur){if (cur->_key < key){cur = cur->_right;}else if (cur->_key > key){cur = cur->_left;}else{//删除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;}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* repalace = cur->_right;Node* repalaceParent = nullptr;while (repalace->_left){   repalaceParent = repalace;repalace = repalace->_left;}cur->_key = repalace->_key;repalaceParent->_left = repalace->_right;delete repalace;}return true;}}
}

 测试用例

#include"FileName.h"
int main()
{BSTree<int> t;int a[] = { 8,3,1,10,1,6,4,7,14,13 };for (auto e : a){t.Insert(e);}t.InOrder();t.Erase(3);cout << endl;t.InOrder();return 0;
}

2.3.5 完整代码

#pragma once
#include<iostream>
using namespace std;
template<class K>
struct BSTNode
{K _key;BSTNode<K>* _left;BSTNode<K>* _right;BSTNode(const K& key):_key(key), _left(nullptr), _right(nullptr){}};
template<class K>
class BSTree
{using Node = BSTNode<K>;//等价于 typedef Node BsTNode<K> ;public:~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;//用parent找到满足搜索二叉树的节点进行插入//cur负责查找位置//像这种的逻辑上连续,物理上不连续的链式结构一般都用双指针解决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;}//提供Get()函数,访问内部私有函数void InOrder(){_InOrder( _root);}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 Erase(const K& key){Node* parent = _root;Node* cur = _root;while (cur){if (cur->_key < key){cur = cur->_right;}else if (cur->_key > key){cur = cur->_left;}else{//删除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;}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* repalace = cur->_right;Node* repalaceParent = nullptr;while (repalace->_left){   repalaceParent = repalace;repalace = repalace->_left;}cur->_key = repalace->_key;repalaceParent->_left = repalace->_right;delete repalace;}return true;}}}
private:void Destroy(Node* root){if (root == nullptr)return;Destroy(root->_left);Destroy(root->_right);delete root;}void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);}
private:Node* _root = nullptr;
};

3. ⼆叉搜索树key和key/value使⽤场景


3.1 key搜索场景:


只有key作为关键码,结构中只需要存储key即可,关键码即为需要搜索到的值,搜索场景只需要判断key在不在。key的搜索场景实现的⼆叉树搜索树支持增删查,但是不支持修改,修改key破坏搜索树结构了。


场景1:小区无人值守车库,小区车库买了车位的业主车才能进小区,那么物业会把买了车位的业主的车牌号录入后台系统,车辆进入时扫描车牌在不在系统中,在则抬杆,不在则提示非本小区车辆,无法进⼊。
场景2:检查一篇英文文章单词拼写是否正确,将词库中所有单词放入二叉搜索树,读取文章中的单词,查找是否在⼆叉搜索树中,不在则波浪线标红提⽰。

 3.2 key/value搜索场景:


每⼀个关键码key,都有与之对应的值value,value可以任意类型对象。树的结构中(结点)除了需要存储key还要存储对应的value,增/删/查还是以key为关键字走二叉搜索树的规则进行比较,可以快速查找到key对应的value。key/value的搜索场景实现的二叉树搜索树支持修改,但是不支持修改key,修改key破坏搜索树结构了,可以修改value。


场景1:简单中英互译字典,树的结构中(结点)存储key(英文)和vlaue(中文),搜索时输入英文,则同时查找到了英文对应的中文。
场景2:商场无人值守车库,入口进场时扫描车牌,记录车牌和入场时间,出口离场时,扫描车牌,查找入场时间,⽤当前时间-入场时间计算出停车时长,计算出停车费用,缴费后抬杆,车辆离场。
场景3:统计一篇文章中单词出现的次数,读取⼀个单词,查找单词是否存在,不存在这个说明第一次出现,(单词,1),单词存在,则++单词对应的次数。

查字典

#pragma once
#include<iostream>
using namespace std;
namespace key {template<class K, class V>struct BSTNode{K _key;V _value;BSTNode<K, V>* _left;BSTNode<K, V>* _right;BSTNode(const K& key, const V& value):_key(key), _value(value), _left(nullptr), _right(nullptr){}};template<class K, class V>class BSTree{using Node = BSTNode<K, V>;//等价于 typedef Node BsTNode<K> ;public://强制生成构造BSTree() = default;BSTree(const BSTree& t){_root = Copy(t._root);}BSTree& operator=(BSTree tmp){swap(_root, tmp._root);return *this;}Node* Copy(Node* root){if (root == nullptr)return nullptr;Node* newRoot = new Node(root->_key, root->_value);newRoot->_left = Copy(root->_left);newRoot->_right = Copy(root->_right);return newRoot;}~BSTree(){Destroy(_root);_root = nullptr;}bool Insert(const K& key,const V& _value){if (_root == nullptr){_root = new Node(key,_value);return true;}Node* parent = nullptr;Node* cur = _root;//用parent找到满足搜索二叉树的节点进行插入//cur负责查找位置//像这种的逻辑上连续,物理上不连续的链式结构一般都用双指针解决while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else//元素已在树中{return cur;}}cur = new Node(key,_value);if (parent->_key < key){parent->_right = cur;}else{parent->_left = cur;}return true;}//提供Get()函数,访问内部私有函数void InOrder(){_InOrder(_root);}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 = _root;Node* cur = _root;while (cur){if (cur->_key < key){cur = cur->_right;}else if (cur->_key > key){cur = cur->_left;}else{//删除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;}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* repalace = cur->_right;Node* repalaceParent = nullptr;while (repalace->_left){repalaceParent = repalace;repalace = repalace->_left;}cur->_key = repalace->_key;repalaceParent->_left = repalace->_right;delete repalace;}return true;}}}private:void Destroy(Node* root){if (root == nullptr)return;Destroy(root->_left);Destroy(root->_right);delete root;}void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);cout << root->_key << ":" << _root->_value << endl;_InOrder(root->_right);}private:Node* _root = nullptr;};
}

#include"FileName.h"
int main()
{key::BSTree<string, string> dict;//BSTree<string, string> copy = dict;dict.Insert("left", "左边");dict.Insert("right", "右边");dict.Insert("insert", "插⼊");dict.Insert("string", "字符串");string str;while (cin >> str){auto ret = dict.Find(str);if (ret){cout << "->" << ret->_value << endl;}else{cout << "无此单词,请重新输入" << endl;}}return 0;}

 查水果数量

#include"FileName.h"
int main()
{string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜","苹果", "香蕉", "苹果", "香蕉" };key::BSTree<string, int> countTree;for (const auto& str : arr){// 先查找⽔果在不在搜索树中// 1、不在,说明⽔果第⼀次出现,则插⼊<⽔果, 1>// 2、在,则查找到的结点中⽔果对应的次数++//BSTreeNode<string, int>* ret = countTree.Find(str);auto ret = countTree.Find(str);if (ret == NULL){countTree.Insert(str, 1);}else{ret->_value++;}}countTree.InOrder();return 0;
}

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

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

相关文章

el-table表格点击该行任意位置时也勾选上其前面的复选框

需求&#xff1a;当双击表格某一行任意位置时&#xff0c;自动勾选上其前面的复选框 1、在el-table 组件的每一行添加row-dblclick事件&#xff0c;用于双击点击 <el-table:data"tableData"ref"tableRef"selection-change"handleSelectionChange&q…

如何在Chrome最新浏览器中调用ActiveX控件?

小编最近登陆工商银行网上银行&#xff0c;发现工商银行的个人网银网页&#xff0c;由于使用了ActiveX安全控件&#xff0c;导致不能用高版本Chrome浏览器打开&#xff0c;目前只有使用IE或基于IE内核的浏览器才能正常登录网上银行&#xff0c;而IE已经彻底停止更新了&#xff…

AI绘图网页版工具

https://chat.bushao.info/?inVitecodeCHBEPQQOOM 一款AI绘图工具&#xff0c;很好玩&#xff0c;推荐&#xff1b; 我自己根据文本生成的图&#xff0c;感觉还不错。

ROC、TPR、FPR的含义

1、ROC&#xff08;Receiver Operating Characteristic&#xff09; ROC&#xff08;Receiver Operating Characteristic&#xff09;曲线是一种用于评估分类模型性能的工具。它通过绘制真阳性率&#xff08;True Positive Rate, TPR&#xff09;与假阳性率&#xff08;False…

仪表放大器AD620

AD623 是一款低功耗、高精度的仪表放大器&#xff0c;而不是轨到轨运算放大器。它的输入电压范围并不覆盖整个电源电压&#xff08;轨到轨&#xff09;&#xff0c;但在单电源供电下可以处理接近地电位的输入信号。 AD620 和 AD623 都是仪表放大器&#xff0c;但它们在一些关键…

vscode【实用插件】Notes 便捷做笔记

安装 在 vscode 插件市场的搜索 Notes点 安装 安装成功后&#xff0c;vscode 左侧栏会出现 使用 初次使用 需先选择一个本地目录 重启 vscode 后&#xff0c;得到 切换笔记目录 新建笔记 快捷键为 Alt N 默认会创建 .md 文件 配合插件 Markdown Preview Enhanced 预览 .md…

2024中国新能源汽车零部件交易会,开源网安展示了什么?

近日&#xff0c;2024中国新能源汽车零部件交易会在十堰国际会展中心举行。开源网安车联网安全实验室携车联网安全相关产品及解决方案亮相本次交易会&#xff0c;保障智能网联汽车“车、路、云、网、图、边”安全&#xff0c;推动智能网联汽车技术突破与产业化发展。 中国新能源…

【深度学习】(7)--神经网络之保存最优模型

文章目录 保存最优模型一、两种保存方法1. 保存模型参数2. 保存完整模型 二、迭代模型 总结 保存最优模型 我们在迭代模型训练时&#xff0c;随着次数初始的增多&#xff0c;模型的准确率会逐渐的上升&#xff0c;但是同时也随着迭代次数越来越多&#xff0c;由于模型会开始学…

【从0开始自动驾驶】用python做一个简单的自动驾驶仿真可视化界面

【从0开始自动驾驶】用python做一个简单的自动驾驶仿真可视化界面 废话几句废话不多说&#xff0c;直接上源码目录结构init.pysimulator.pysimple_simulator_app.pyvehicle_config.json 废话几句 自动驾驶开发离不开仿真软件成品仿真软件种类多https://zhuanlan.zhihu.com/p/3…

【CSS】鼠标 、轮廓线 、 滤镜 、 堆叠层级

cursor 鼠标outline 轮廓线filter 滤镜z-index 堆叠层级 cursor 鼠标 值说明值说明crosshair十字准线s-resize向下改变大小pointer \ hand手形e-resize向右改变大小wait表或沙漏w-resize向左改变大小help问号或气球ne-resize向上右改变大小no-drop无法释放nw-resize向上左改变…

蓝桥杯1.小蓝的漆房

样例输入 2 5 2 1 1 2 2 1 6 2 1 2 2 3 3 3样例输出 1 2 import math import os import sys tint(input())#执行的次数 for j in range(t):n,kmap(int,input().split())#n为房间数 k为一次能涂的个数alist(map(int,input().split()))#以列表的形式存放房间的颜色maxvaluemath…

处理RabbitMQ连接和认证问题

在使用RabbitMQ进行消息队列管理时&#xff0c;我们可能会遇到各种连接和认证问题。本文将介绍如何诊断和解决这些问题&#xff0c;并通过使用RabbitMQ的管理端进行登录验证来确保配置正确。 1. 问题概述 在最近的一次部署中&#xff0c;我们遇到了两个主要问题&#xff1a; …

IPSec隧道协议学习(一)

前情回顾 前面介绍的GRE隧道协议&#xff0c;可以字LAN之间通过Internet建立隧道&#xff0c;实现网络间资源共享&#xff0c;但是GRE隧道协议不能实现加密功能&#xff0c;传输的数据不受加密保护&#xff0c;为了实现在隧道间传输数据包收到加密保护&#xff0c;需要使用IPS…

GitLab发送邮件功能详解:如何配置自动化?

GitLab发送邮件的设置指南&#xff1f;怎么优化GitLab发送邮件&#xff1f; GitLab作为一个强大的代码管理平台&#xff0c;不仅提供了代码托管、CI/CD等功能&#xff0c;还集成了发送邮件的功能&#xff0c;使得开发团队能够及时获取项目动态。AokSend将详细介绍如何配置GitL…

代码随想录 -- 回溯 -- 非递减子序列

491. 非递减子序列 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a;重点是去重 收集结果&#xff1a;每次进入递归先判断path中的元素数量&#xff0c;如果大于1了&#xff0c;就将path收集到result中。 递归参数&#xff1a;nums&#xff0c;index&#xff0c;pa…

2024 go-zero社交项目实战

背景 一位商业大亨&#xff0c;他非常看好国内的社交产品赛道&#xff0c;想要造一款属于的社交产品&#xff0c;于是他找到了负责软件研发的小明。 小明跟张三一拍即合&#xff0c;小明决定跟张三大干一番。 社交产品MVP版本需求 MVP指&#xff1a;Minimum Viable Product&…

职场能力强的人都在做什么---今日头条

【职场里,能力强的人都在做哪些事... - 今日头条】https://m.toutiao.com/is/ikn6kt9q/ 知识雷达 2024-09-21 16:33 目录 职场里,能力强的人都在做哪些事呢? 1、复盘; 2、多角度思考;3、记录信息; 4、永远积极主动;5、主动获取信息差; 6、明确人和人的关系;7、…

【Altium Designer程序开发】BGA芯片自动扇出

BGA自动扇出功能支持将BGA器件从4个方向上扇出&#xff0c;里面有无空白区域均可支持&#xff0c;执行速度非常快&#xff0c;通常在秒级的时间内即可处理完成&#xff0c;程序可以通过以下几种方式启动。 ➡️支持从菜单栏启动 ➡️支持从工具栏启动 ➡️支持从服务器面板启动…

Go weak包前瞻:弱指针为内存管理带来新选择

在介绍Go 1.23引入的unique包的《Go unique包&#xff1a;突破字符串局限的通用值Interning技术实现》一文中&#xff0c;我们知道了unique包底层是基于internal/weak包实现的&#xff0c;internal/weak是一个弱指针功能的Go实现。所谓弱指针(Weak Pointer&#xff0c;也称为弱…

HarmonyOS鸿蒙开发实战(5.0)自定义路由栈管理

鸿蒙HarmonyOS NEXT开发实战往期文章必看&#xff08;持续更新......&#xff09; HarmonyOS NEXT应用开发性能实践总结 HarmonyOS NEXT应用开发案例实践总结合集 最新版&#xff01;“非常详细的” 鸿蒙HarmonyOS Next应用开发学习路线&#xff01;&#xff08;从零基础入门…