二叉树搜索树(上)

二叉树搜索树(上)

在这里插入图片描述

概念

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

• 若它的左子树不为空,则左子树上所有结点的值都于等于根结点的值

• 若它的右子树不为空,则右子树上所有结点的值都于等于根结点的值

• 它的左右子树也分别为二叉搜索树

• 二叉搜索树中可以支持插⼊相等的值,也可以不支持插⼊相等的值,具体看使⽤场景定义,map/set/multimap/multiset系列容器底层就是二叉搜索树,其中map/set不支持插⼊相等值,multimap/multiset支持插⼊相等值

二叉搜索树的性能分析

最优情况下,二叉搜索树为完全二叉树(或者接近完全二叉树),其⾼度为: O ( l o g 2 N ) O(log_2N) O(log2N)

最差情况下,二叉搜索树退化为单支树(或者类似单支),其⾼度为: O ( N 2 ) O(\frac{N}{2}) O(2N)

所以综合而言二叉搜索树增删查改时间复杂度为: O(N)

那么这样的效率显然是无法满⾜我们需求的,二叉搜索树的变形:平衡二叉搜索树AVL树和红⿊树,才能适用于我们在内存中存储和搜索数据。

另外需要说明的是,二分查找也可以实现 O(logN) 级别的查找效率,但是二分查找有两⼤缺陷:

  1. 需要存储在支持下标随机访问的结构中,并且有序。
  2. 插⼊和删除数据效率很低,因为存储在下标随机访问的结构中,插⼊和删除数据⼀般需要挪动数据。

这⾥也就体现出了平衡二叉搜索树的价值。

二叉树的插入

插⼊的具体过程如下:

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

参考代码:

template<class K>
class BSTNode
{
public:BSTNode(const K& key):_key(key),_left(nullptr),_right(nullptr){}private:K _key;BTSNode<K>* _left;BTSNode<K>* _right;};template<class K>
class BSTree
{using Node = BSTNode<k>;//用using替代typedef的作用
public:bool Insert(const K& key){//如果是空树if (_root == nullptr){_root = new Node(key);return true;//这时就结束了}//如果不是空树Node* parent = nullptr;Node* cur = _root;while (cur)//当cur不为空{if (key < cur->_key){parent = cur;cur = cur->_left;}else if (key > cur->_key){parent = cur;cur = cur->_right;}elsereturn false;//避免冗余}//到这里说明cur已经找到空位置了,但是不知道是从右走的还是往左走的,得再比一次cur = new Node(key);if (key < parent->_key){parent->left = cur;}else{parent->right = cur;}return true;}
private:Node* _root = nullptr;//是结点指针类型,不是结点类型
};

如果允许已存在的值插入(或者说运行冗余):

我们会发现,如果走中序遍历(中序遍历顺序是先遍历左子树,然后访问根节点,最后遍历右子树),就是有序的。即使允许冗余的情况下,中序遍历也是有序的。

中序遍历代码参考:

template<class K>
class BSTree
{
//……private:void _InOrder(Node* root){if (root == nullptr)return;_InOrder(root->_left);cout << root->_key <<" ";_InOrder(root->_right);}Node* _root = nullptr;//是结点指针类型,不是结点类型
};

可以发现一个问题,我们需要传入根节点才能进行中序遍历,但是根节点在类中贝private修饰无法访问。

一种方法是提供GetRoot接口,但还有别的方法:

在C++的类中要写递归时,公开的方法都要套一层。

public:
void InOrder()//类外面拿不到_root,但是类里面是可以的
{_InOrder(_root);
}

类里面二叉树的递归基本都需要像这样套一层。

二叉树搜索树的查找

  1. 从根开始⽐较,查找x,x⽐根的值⼤则往右边⾛查找,x⽐根值⼩则往左边⾛查找。
  2. 最多查找⾼度次,⾛到到空,还没找到,这个值不存在。
  3. 如果不⽀持插⼊相等的值,找到x即可返回
  4. 如果⽀持插⼊相等的值,意味着有多个x存在,⼀般要求查找中序的第⼀个x。如下图,查找3,要找到1的右孩⼦的那个3返回

参考代码:

bool Find(const K& key)
{Node* cur = _root;while (cur){if (key < cur->_key){cur = cur->_left;}else if (key > cur->_key){cur = cur->_right;}else{return true;}}return false;
}

二叉搜索树的删除

⾸先查找元素是否在二叉搜索树中,如果不存在,则返回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* cur = _root;Node* parent = nullptr;while (cur){if (key < cur->_key){parent = cur;cur = cur->_left;}else if(key>cur->_key){parent = cur;cur = cur->_right;}else{//找到了要删除的数据,接下来进行删除,但是要判断情况//cur的左孩子为空(左右孩子都为空也被包含在内了),然后把cur的右孩子给给父(父可能为空)if (cur->_left == nullptr){//父为空,也就是说要删的是头结点if (parent==nullptr){_root = cur->_right;}else{//要判断父节点的左还是右指针指向cur的孩子if(parent->_left==cur){parent->_left = cur->_right;}else{parent->_right = cur->_right;}}delete cur;return true;//别忘了}//cur的右孩子为空,把cur的左孩子给父else if (cur->_right == nullptr){if (parent == nullptr){_root = cur->_left;}else{//判断父节点的左还是右指针指向cur孩子if (parent->_left == cur){parent->_left = cur->_left;}else{parent->_right = cur->_left;}}delete cur;return true;//别忘了}else//现在是cur的左右孩子都不为空{//第一步,找R(这里采用cur的右子树的最小结点/也就是最左结点)Node* p = cur;//这里如果初始给空,后面如果不进循环,会造成对空指针的解引用Node* r = cur->_right;while (r->_left){p = r;r = r->_left;}//第二步,进行交换(但不用真的把cur的值再去给r)cur->_key = r->_key;//第三步,删除R——很容易考虑不全面!!删除R又要把删除的问题考虑全面,但这里不能用递归,会找不到//在删除R时要把R的右孩子(只可能是右孩子)给给父,但是父的左还是右指针不确定if (p->_right == r){p->_right = r->_right;}else{p->_left = r->_right;}delete r;return true;}}}return false;
}

那么,增删查改我们现在已经完成了增、删、查了,但是普通二叉树是不允许修改的。

本文到此结束。

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

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

相关文章

解读Nature:Larger and more instructable language models become less reliable

目录 Larger and more instructable language models become less reliable 核心描述 核心原理 创新点 举例说明 大模型训练,微调建议 Larger and more instructable language models become less reliable 这篇论文的核心在于对大型语言模型(LLMs)的可靠性进行了深入…

在Linux上部署(MySQL Redis Elasticsearch等)各类软件

实战章节&#xff1a;在Linux上部署各类软件 前言 为什么学习各类软件在Linux上的部署 在前面&#xff0c;我们学习了许多的Linux命令和高级技巧&#xff0c;这些知识点比较零散&#xff0c;同学们跟随着课程的内容进行练习虽然可以基础掌握这些命令和技巧的使用&#xff0c…

局域网桥接只能单向ping问题,arp无法建立

一、问题 三台设备&#xff1a;Windows&#xff0c;Ubuntu&#xff0c;开发板。 我的Windows在每次开机后&#xff0c;无法ping通开发板&#xff0c;开发板可以ping通Windows&#xff1b; Windows和另一台局域网内的Ubuntu可以相互ping通&#xff1b; Ubuntu和开发板可以相互pi…

FPGA实现以太网(二)、初始化和配置PHY芯片

系列文章目录 FPGA实现以太网&#xff08;一&#xff09;、以太网基础知识 文章目录 系列文章目录一、MDIO协议介绍二、PHY芯片管脚以及结构框图三、MDIO帧时序介绍3.1 MDIO帧格式3.2 MDIO写时序3.3 MDIO读时序 四、PHY芯片常用寄存器描述4.1 基本模式控制寄存器&#xff08;0…

Spring资源加载模块,原来XML就这,活该被注解踩在脚下 手写Spring第六篇了

这一篇让我想起来学习 Spring 的时&#xff0c;被 XML 支配的恐惧。明明是写Java&#xff0c;为啥要搞个XML呢&#xff1f;大佬们永远不知道&#xff0c;我认为最难的是 XML 头&#xff0c;但凡 Spring 用 JSON来做配置文件&#xff0c;Java 界都有可能再诞生一个扛把子。 <…

Unity WebGL交互通信

Unity 调用 H5 本文使用的 unity 版本为&#xff1a;2021.3.3 1.在unity中通过c#的特性DllImport导出外部实现函数 [DllImport("__Internal")]private static extern void callJsString(string param);[DllImport("__Internal")]private static extern vo…

Android 如何实现不编译指定的apk,不加载系统应用

1.把Android.mk改为Android.mk_bak 2.删除当前Android.mk内容变为空mk 或者注释掉里面所有内容 3.以上方法存在些许问题&#xff0c;因为只是把当前的mk屏蔽了&#xff0c;但其他路径的类似应用也会编译进去。 在内置应用mk下添加需要覆盖的应用&#xff0c;这个比较全面&…

SpringCloud框架学习(第二部分:Consul、LoadBalancer和openFeign)

目录 六、Consul服务注册和发现 1.基本介绍 2.下载运行 3.服务注册与发现 &#xff08;1&#xff09;支付服务provider8001注册进consul &#xff08;2&#xff09;修改订单服务cloud-consumer-order80 4.CAP &#xff08;1&#xff09;CAP理论 &#xff08;2&#x…

[每日一练]通过自连接实现混合列的筛选(pandas解法)

#该题目来源于力扣&#xff1a; 1241. 每个帖子的评论数 - 力扣&#xff08;LeetCode&#xff09; 题目要求&#xff1a; 表 Submissions &#xff1a;------------------------- | 列名 | 类型 | ------------------------- | sub_id | int | | pa…

Playwright——快速入门(初章)

Playwright&#xff1a;引领自动化测试的未来 在数字化时代&#xff0c;Web应用的复杂性和用户期望的提高对软件测试提出了更高的要求。微软开发的Playwright&#xff0c;作为一个创新的自动化测试框架&#xff0c;正以其卓越的性能和全面的浏览器支持&#xff0c;重新定义Web…

ssm094学生宿舍管理+jsp(论文+源码)_kaic

毕 业 设 计&#xff08;论 文&#xff09; 题目&#xff1a;学生宿舍管理系统设计与实现 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本学生宿舍管理系统…

Redis中的过期删除与内存淘汰

1.Redis中的过期删除策略 在 Redis 中&#xff0c;过期删除策略是为了管理存储在 Redis 中的带有过期时间的数据。每当数据存储时&#xff0c;可能会为其设定一个过期时间。当到达这个时间点后&#xff0c;该数据就被标记为“过期”。为了确保不再需要的过期数据不会占用系统资…

Kubernetes 中的 GPU 管理与 Device Plugin 机制深度解析

Kubernetes 中的 GPU 管理与 Device Plugin 机制深度解析 在现代计算领域,GPU(图形处理器)的强大计算能力越来越受到重视。Kubernetes 作为一种流行的容器编排平台,也提供了对 GPU 资源的管理和使用支持。本节课将深入剖析 Kubernetes 中的 GPU 管理与 Device Plugin 机制…

机器学习——贝叶斯

&#x1f33a;历史文章列表&#x1f33a; 机器学习——损失函数、代价函数、KL散度机器学习——特征工程、正则化、强化学习机器学习——常见算法汇总机器学习——感知机、MLP、SVM机器学习——KNN机器学习——贝叶斯机器学习——决策树机器学习——随机森林、Bagging、Boostin…

Leetcode 3352. Count K-Reducible Numbers Less Than N

Leetcode 3352. Count K-Reducible Numbers Less Than N 1. 解题思路2. 代码实现 题目链接&#xff1a;3352. Count K-Reducible Numbers Less Than N 1. 解题思路 这一题的话思路上我是拆成了两步来做的&#xff0c;首先&#xff0c;我们要认识到&#xff0c;这里的变化本质…

403 Request Entity Too Lager(请求体太大啦)

昨天收到 QA 的生产报障&#xff0c;说是测试环境的附件上传功能报了 403 的错误&#xff0c;错误信息&#xff1a;403 Request Entity Too Lager。我尝试复现问题&#xff0c;发现传个几兆的文件都费劲啊&#xff0c;一传一个失败。不用说&#xff0c;项目用到 ng 代理&#x…

232转485模块测试

概述 常用的PLC一般会有两个左右的232口&#xff0c;以及两个左右的485口&#xff0c;CAN口等&#xff0c;但是PLC一般控制的设备可能会有很多&#xff0c;会超出通讯口的数量&#xff0c;此时我们一般会采用一个口接多个设备&#xff0c;这种情况下要注意干扰等因素&#xff0…

科技资讯|Matter 1.4 标准正式发布,低功耗蓝牙助力其发展

连接标准联盟&#xff08;CSA&#xff09;宣布推出最新的 Matter 1.4 版本&#xff0c;引入了一系列新的设备类型和功能增强&#xff0c;有望提高包括 HomeKit 在内的智能家居生态系统之间的互操作性。 设备供应商和平台能够依靠增强的多管理员功能改善多生态系统下的用户体验&…

@ComponentScan注解引发外部请求无法处理的解决办法

ComponentScan注解引发外部请求无法处理的解决办法 问题起因 最近通过maven从项目中拆分出模块module-db和模块module-seckill 模块module-db主要通过mybatisplus实现数据库的操作&#xff0c;并封装业务接口&#xff1b; 模块module-seckill引入module-db依赖&#xff0c;…

SpringBoot实现文件上传并返回url链接

检查依赖 确保pom.xml包含了Spring Boot Web的依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>创建Controller 创建公用上传文件控制器 package…