数据结构+算法(第11篇) :无死角“盘”它!二分查找树

作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬

学习必须往深处挖,挖的越深,基础越扎实!

阶段1、深入多线程

阶段2、深入多线程设计模式

阶段3、深入juc源码解析


阶段4、深入jdk其余源码解析


阶段5、深入jvm源码解析

码哥源码部分

码哥讲源码-原理源码篇【2024年最新大厂关于线程池使用的场景题】

码哥讲源码【炸雷啦!炸雷啦!黄光头他终于跑路啦!】

码哥讲源码-【jvm课程前置知识及c/c++调试环境搭建】

​​​​​​码哥讲源码-原理源码篇【揭秘join方法的唤醒本质上决定于jvm的底层析构函数】

码哥源码-原理源码篇【Doug Lea为什么要将成员变量赋值给局部变量后再操作?】

码哥讲源码【你水不是你的错,但是你胡说八道就是你不对了!】

码哥讲源码【谁再说Spring不支持多线程事务,你给我抽他!】

终结B站没人能讲清楚红黑树的历史,不服等你来踢馆!

打脸系列【020-3小时讲解MESI协议和volatile之间的关系,那些将x86下的验证结果当作最终结果的水货们请闭嘴】

 

引言

《菜鸟也能“种”好二叉树!》一文中提到了:为了方便查找,需要进行分层分类整理。而满足这种目标的数据结构之一就是树。

树的叶子节点可以看作是最终要搜寻的目标物;叶子节点以上的每一层,都可以看作是一个大类别、层中的每个节点都可以看作是一个小类别。

从上图可以看出,要定位目标物,就需要从最上面的大类依次向下定位目标物所属的小类。

定位的效率(时间复杂度)取决于两个因素:

  1. 非叶子节点的分岔数:分岔数越多,表示大类包含的小类数目也就越多,那么为了定位到底属于哪个小类,比较次数也就越多,从而时间开销也就越大。
  2. 树的高度(或称为深度):树越深(高),从根节点(最大类)到叶子节点(目标物)的路径也就越长,也就意味着时间开销越大。

研究问题都讲究由简到繁,那就让我们先来看看最简单的情形——分岔数最小的情形——二叉树。

二叉树的每层节点只有两个节点,这表示只有两个小类。定位属于哪个小类时,需要做比较。比较的次数越少、比较的方法越简单,效率也就越高。

比较次数再怎么少也得1次、最简单的比较方法就是比大小。为了满足这个目标,前辈们就对一般二叉树加了如下规则:

每个非叶子节点的左孩子的值不大于该节点本身的值;右孩子的值不小于该节点本身的值。

这样的二叉树就称为“二分查找树”。

二分查找树的数学思想

将二分查找树从根节点(最大类)到叶子节点(目标物)的路径扒出来,垂直放置之后就如下图左部所示。再倒”下来水平放置之后,就如下图右部所示。

由此可以看出,从最大类到目标物的查找过程,其实就是从大类不断逼近目标物的过程。

这个思想的本质其实就是数学的“逼近法”——不断缩小范围、直至不可再小,最终剩下的即为所求。

“逼近法”思想大量在数学中应用。牛顿当年发明微积分,其证明过程其实采用的也是“逼近法”。具体可以参见牛顿的旷世巨著《自然哲学的数学原理》第一编《物体的运动》的第1章《初量与终量的比值方法》的引理2。

牛顿

《自然哲学的数学原理》

二分查找法

基于二分查找树数据结构的搜索算法称为“二分查找法”。

二分查找树是一个递归定义,所以很容易得出递归版的二分查找法。

下面以链表形式存储的二分查找树为例,数组形式存储的,可以根据父子节点下标的线性关系(《菜鸟也能“种”好二叉树!》一文中的推论5.2.1),类似推导,在此就不赘述了。

还是根据《史上最猛之递归屠龙奥义》一文中的老套路,转换成非递归版本:

整个算法的时间开销主要由do-while循环体的循环次数决定。很显然,在最坏情况下,循环次数等于二叉查找树的高度。假设树的节点总数为N,则根据《菜鸟也能“种”好二叉树!》一文中的结论,高度等于logN,从而时间复杂度等于O(logN)。

二分查找树的节点插入算法

向二分查找树插入新节点很简单,从根节点开始,根据定义逐层比较、进入对应子树下沉、直至叶子节点:

对应的递归版算法代码如下:

还是根据《史上最猛之递归屠龙奥义》一文中的老套路,转换成非递归版本:


可以看出,整个算法结构与二分查找树的搜索算法类似,时间复杂度也是O(logN)。

二分查找树的节点删除算法

直接删除节点,会破坏二叉树的结构,需要进行调整。

首先需要有节点补上被删节点的空缺。这个“补漏”有两个策略:

  1. 直接计算出到底哪个节点最终应该到这个位置
  2. 先用一个节点顶上,然后再进行下推调整

稍微想一想,就会知道第一种策略比较复杂,因为你需要在一开始就通盘考虑,复杂度很高;

第二种策略其实是一种局部性原理思想——先局部求解、再逐步递进到全局解。这种局部性原理思想在整个计算机科学中大量使用:比如虚拟内存管理、人工智能的爬山算法等等。

第二种策略其实我们在上一篇《二叉堆“功夫熊猫”的速成之路》中的“Top N”章节中也提到了。有兴趣的朋友也可以翻回去看看。

具体实操上,和“Top N”的方法一样,我们用尾节点“补漏”被删节点。

上面三张图形象描绘了整个替换、下推调整的过程。

这里啰嗦一句:因为要先得到尾节点的位置,然后再回到待删节点位置——这涉及到遍历和回溯,若采用链表存储整个二叉查找树的话,就不是很方便。所以针对节点删除场景,用数组更简单。

但为了“炫技”,笔者在这里就挑最复杂的单向链表式、非递归版算法来实现一下:)

最坏情况无外乎删除根节点——这种情况下下推的距离最长——极限情况下,要下推整个二分查找树的高度。所以这个算法的时间复杂度不超过O(logN)。

至于数组式、递归版算法,读者可以根据《史上最猛之递归屠龙奥义》和《二叉堆“功夫熊猫”的速成之路》中讲到的套路,自行推导。

做一棵“稳重的”二分查找树

上面两棵二分查找树是等价的,但是可以很明显看出:第一棵一些分支会向一边倾斜,而第二棵就显得“稳重”多了。

试想,你要搜索值为17的节点。按照前面二分查找树的搜索算法,对于第一棵树,从根节点开始,一共需要进行4次比较才能找到;而对于第二棵树,只需要进行1次比较就能找到!

为什么会有这么大的差别呢?

答案在于:第二棵树是一棵“平衡二叉树”,它的“稳重”特点实现了一个目标——平均查找长度最短。

下一篇文章我们就来“盘盘”平衡二叉树。

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

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

相关文章

05 MyBatis之表关系的声明+事务+SqlSession三件套的作用域

MyBatis 支持一对一,一对多,多对多查询。XML 文件和注解都能实现关系的操作。多对多实质就是一对多 1. 表关系的维护 1.1 One一对一 一对一查询和多表(两表)查询很相似, 都能查询两表的全部属性 区别是一对一可以在对象中嵌套对象, 呈现包含关系; 多表…

在Linux中对Nginx进行安全加固

准备工作 在IP为x.x.x.x的服务器上安装nginx,确保Linux系统为nginx环境。 检查nginx是否配置nginx账号锁定策略 配置nginx账号锁定策略,降低被攻击概率。 第一步,查看nginx的锁定状态。 命令:passwd -S nginx 若结果出现“P…

安装配置sqoop

一、了解Sqoop 1、Sqoop产生的原因 A. 多数使用hadoop技术的处理大数据业务的企业,有大量的数据存储在关系型数据中。 B. 由于没有工具支持,对hadoop和关系型数据库之间数据传输是一个很困难的事。 以上是sqoop产生的主要原因,也因此Sqoop主要用于hadoop与关系型数据库之…

数据湖系列之二 | 打造无限扩展的云存储系统,元数据存储底座的设计和实践

海量数据对数据湖存储的扩展能力提出了极高的要求。元数据面作为云存储最核心、最底层的系统之一,直接决定了存储系统的扩展性。 本文作为数据湖系列的第二篇,将为大家揭开元数据面存储底座的秘密,如何设计能够支撑存储容量的“无限扩展”。…

监测Tomcat项目宕机重启脚本(Linux)

1.准备好写好的脚本 #!/bin/sh # 获取tomcat的PID TOMCAT_PID$(ps -ef | grep tomcat | grep -v tomcatMonitor |grep -v grep | awk {print $2}) # tomcat的启动文件位置 START_TOMCAT/mnt/tomcat/bin/startup.sh # 需要监测的一个GET请求地址 MONITOR_URLhttp://localhost:…

Vue3_基础使用_2

这节主要介绍:标签和组件的ref属性,父子组件间的传递值,ts的接口定义,vue3的生命周期 1.标签的ref属性。 1.1ref属性就是给标签打标识用的,相当于html的id,但是在vue3中用id可能会乱,下面是ref…

卸载Ubuntu双系统

卸载Ubuntu双系统 我们卸载Ubuntu双系统,可能出于以下原因: 1、Ubuntu系统内核损坏无法正常进入 2、Ubuntu系统分配空间不足,直接扩区较为复杂 3、以后不再使用Ubuntu,清理留出空间 123无论出于哪种原因,我们都是要…

Power BI案例-链接Mysql方法

Power BI案例-连锁Mysql 方法1-通过组件mysql-connector-net-8.3.0: 选择文件–获取数据–选择MySQL数据库–选择链接 提示无组件,选择了解详细情况 弹出浏览器,选择下载 不用登陆,可以直接下载 下载的组件如下&#xff1a…

2024年数学建模美赛 A~E 题目解析

2024美赛A题:资源可用性和性别比例 背景 尽管一些动物物种不属于通常的雄性或雌性,大多数物种在出生时要么显著地为雄性,要么为雌性。虽然许多物种在出生时表现出1:1的性别比,但其他物种则偏离了这个均衡的性别比例。这被称为性…

算法day9

算法day9 栈与队列基础232用栈实现队列225用队列实现栈 栈与队列理论基础 言简意赅:栈的原理就是后进先出。队列就是先进先出。 相关操作: 栈:入栈,出栈,判栈空,取栈顶元素。 队列:出队&#…

什么是ACL?

知识改变命运,技术就是要分享,有问题随时联系,免费答疑,欢迎联系! 厦门微思网络​​​​​​https://www.xmws.cn 华为认证\华为HCIA-Datacom\华为HCIP-Datacom\华为HCIE-Datacom Linux\RHCE\RHCE 9.0\RHCA\ Oracle OC…

Skywalking的Trace Profiling 代码级性能剖析功能应用详解

代码级性能剖析 Skywalking 提供了Trace Profiling功能对具体出现问题的span进行代码级性能剖析。 代码级性能剖析就是利用方法栈快照,并对方法执行情况进行分析和汇总。并结合有限的分布式追踪 span 上下文,对代码执行速度进行估算。性能剖析激活时&a…

Linux系统管理和Shell脚本笔试题

1、写一个sed命令,修改/tmp/input.txt文件的内容,要求:(1) 删除所有空行;(2) 在非空行前面加一个"AAA",在行尾加一个"BBB",即将内容为11111的一行改为:AAA11111BBB #写入内…

自然语言处理(NLP)—— Dialogflow ES聊天机器人

1. 背景介绍 这个实验室的目标是让你了解并使用Google的Dialogflow服务。Dialogflow是一个可以让你创建聊天机器人的服务,这个过程不需要或者只需要很少的编程技能。 1.1 账号的创建 为了完成这个实验室,你需要在以下网站上创建账号&#xff1a…

STM32--USART串口(3)数据包

一、前言 在实际的工程中肯会有同时发送多种数据的情况,比如要不停的发送x、y、z分别对应三种不同的数据。xyzxyzxyz,但接收方可能是从中间某个地方开始接收的,这就导致数据错位。所以我们就需要将数据进行分割,打包成一个一个的…

Request Response 基础篇

Request & Response 在之前的博客中,初最初见到Request和Response对象,是在Servlet的Service方法的参数中,之前隐性地介绍过Request的作用是获取请求数据。通过获取的数据来进行进一步的逻辑处理,然后通过对Response来进行数…

如何搭建私有云盘SeaFile并实现远程访问本地文件资料

🌈个人主页: Aileen_0v0 🔥热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​💫个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-hsDnDEybLME85dTx {font-family:"trebuchet ms",verdana,arial,sans-serif;font-siz…

MySQL 多表查询

重点: MySQL 的 三种安装方式:包安装,二进制安装,源码编译安装。 MySQL 的 基本使用 MySQL 多实例 DDLcreate alter drop DML insert update delete DQL select 3.5)DDL 语句 表:二维关系 设计表&…

python计算两个DataFrame的指定两列中,相同的数据有多少

目的:查询数据1和数据2中,red与red列相同 并且blue与blue列相同的,情况有多少。 (备注:两个数据中格式不一致,需要经过json提取等处理步骤) 思路步骤: 1、读取数据1,筛选…

跨平台开发:浅析uni-app及其他主流APP开发方式

随着智能手机的普及,移动应用程序(APP)的需求不断增长。开发一款优秀的APP,不仅需要考虑功能和用户体验,还需要选择一种适合的开发方式。随着技术的发展,目前有多种主流的APP开发方式可供选择,其…