数据结构(邓俊辉)学习笔记】优先级队列 08——左式堆:结构

文章目录

  • 1. 第一印象
  • 2. 堆之合并
  • 3. 奇中求正
  • 4. NPL
  • 5. 左倾性
  • 6. 左展右敛

1. 第一印象

在学习过常规的完全二叉堆之后,我们再来学习优先级队列的另一变种,也就是左式堆。所谓的左式堆,也就是在拓扑形态上更加倾向于向左侧倾斜的一种堆,

比如这就是一个左式堆由生到长,直至灭亡的整个生命过程。
请添加图片描述
可以看到,相对于常规的完全二叉堆,左式堆的确显得有些别致。

那么,为什么要引入这一新的变种呢?让我们从它的设计动机以及结构定义说起。

2. 堆之合并

在这里插入图片描述

引入左式堆的动机非常直截了当,一言以蔽之,也就是为了能够有效地完成堆合并。具体的,对于任何的堆A 与堆 B,我们如何才能快速地将它们合二为一?

稍加思索,你可能会认为这并不是什么问题,因为借助已掌握的方法,我们完全可以实现这个功能。然而很遗憾,回到我们这门课程的核心,也就是计算效率。我们会发现已有的方法都不足用。

  1. 比如,可能会在两个堆中选择更大的那个作为基础,然后将另一堆中的元素逐一地取出并加入到前者当中,当更小的那个堆缩减为空时,也就自然完成了整体的合并。整个算法的描述也异常的简练,实际上主体的操作完全可以汇总为这样一句 A.insert(B.delMax())。

然而我们很快就会看到这个算法的效率是很低的,为此我们不妨将两个堆的规模分别记作 n 和 m,并且不失一般性地假设前者不小于后者,于是整个算法共需迭代 m 次,在每一次迭代中为了从 b 中摘除最大元,我们需要花费 log m 的时间,而为了将这个元素汇入到 a 中,我们又需要花费 log (n + m) 时间,总体而言我们大致需要 m *log n 的时间。至此,我相信你也会对这个效率不甚满意。是的,因为它的确存在改进的空间与可能。

  1. 是的,或许会想起 Floyd 批量建堆算法,实际上更为高效的一种办法就是将这两个堆中的元素首先简单地混合起来,然后借助 Floyd 算法将这 n + m 个元素整理为一个完全二叉堆,。

你应该记得 ,Floyd 算法只需线性时间,这一算法的效率明显优于此前的那个算法。然而即使是这个线性的效率,也依然不能令我们满意。你能看出我们不满意的原因吗?

没错,作为 Floyd 算法的输入在默认情况下,所有的元素都是无序排列。然而现在却不是这样,事实上它们被分成了两组,而且我们已经分别知道了它们内部的偏序关系,很遗憾刚才这个线性的算法却没有利用到我们已掌握的这些信息。

因此从这一角度出发,我们完全有理由相信应该会存在更为高效的数据结构及相应的算法。事实上,左式堆正是问题的答案。

3. 奇中求正

在这里插入图片描述

事实上,所谓的左式堆,也就是在保持堆序性的前提下,附加某种新的约束条件,从而使得在堆的合并过程中,我们只需调整少量的节点,事实上,只要这种结构及相应的算法设计得当,我们完全可以将所涉及的节点数目控制在 log(n) 的范围内。

  • 那么新引入的约束条件是什么呢?
    一言以蔽之,也就是所谓的单侧倾斜, 比如沿用发明者所确定的惯例,堆中各节点的分布会偏向于左侧,而相关算法之所以能够高效的诀窍在于,所有的合并操作只会涉及到全堆的右侧部分

    比如这就是左式堆的典型图解,所谓的向左侧倾斜,类比于书法,就相当于把撇写得更长,而捺写得更短。准确地讲,左式堆可以将右侧肩部的长度严格地控制在大 O 意义下的 log(n) 以内。可想而知,果真如此的话,我们就可以自然地将整体的时间复杂度控制在 log(n) 的范围之内。相对于我们刚才所介绍的那个线性算法,这不得不说是一个巨大的改进。

  • 那么这样一种特性是如何做到的呢?
    现在回答这个问题还嫌过早,因为我们首先还需要回答另一个问题。我们注意到如果真的有这样一个堆,那么它已经断乎不可能继续是一棵完全二叉树,也就是说尽管在此堆序性还有可能延续,而结构性却已荡然无存,无从谈起了。是的,的确如此。

    然后我们需要明白的是,对于堆结构而言,只有堆序性才是其本质的要求,而结构性却不是,在必要的时候结构性完全是可以牺牲掉的。

4. NPL

在这里插入图片描述

为了度量和判断左式堆的单侧倾斜性,我们需要引入一个概念,也就是所谓的"空节点路径长度"。

为便于讲解和理解,继续沿用之前在红黑树和 B 树中已经多次采用的一个技巧,也就是通过引入足够多个外部节点,将整个堆假想地转换为一棵真二叉树。
~  
比如,在上图中所有深色的节点都是原有的真实存在的节点,而所有浅色,也是方形的节点都是我们假想的引入的外部节点。可以看到经过这样一个转换,的确所有节点的度数都变成了偶数。

那么所谓的空节点路径长度(Null Path Length) 又是如何定义的呢? 首先作为基础,对于刚刚引入的每一个外部节点而言,其 NPL 值都统一设作0,而对于任何一个原本就存在的内部节点而言,为了确定它的 NPL 值,我们需要找到它的左孩子以及右孩子,并且在其中挑选出更小的 NPL 值。在已知的基础增加一个单位。

看到这个定义的表达式,你或许会想起点什么。没错,节点的高度。事实上,只要将这个 min() 换成 max(),也就是不折不扣的高度。通过这样的对比和类比,希望能够对这两个概念有更深刻的认识。

就这里的 NPL 而言,我们不难验证以下两个事实:

  1. 首先,任意节点 x 的NPL 值,必然等于 x 到所有外部节点的最近距离,比如任何一个外部节点的 NPL 值之所以为0,是因为它到所有外部节点的距离就是它自己到自己的距离,这个距离自然是0。

    再来看这个节点(上图第三排由左往右第3个)它的 NPL 值为什么是2呢?因为尽管它有一个外部节点的后代与它的距离为3,但这却不是最近的距离,实际上最近的距离应该实现于这样三个后代外部节点,而且这个距离恰好是2。

    类似地,这个例子中的根节点 NPL 值之所以为3,是因为这些外部节点到它的距离最近,而且这个距离都是3。

  2. 另一个可以验证的事实是,对于任何一个 x 而言,它的 NPL 值也是以它为根的那棵最大满树的高度。

    回到我们刚才的这个实例,依然考察 NPL 值为2的这个节点(上图第三排由左往右第3个),它的 NPL 值为2,我们也可以理解为存在一棵以它为根的高度为2的满树。

    事实上,对于其他的节点,也是如此:这个节点的 NPL 值为2(上图第二排由左往右第1个),是因为以它为根的极大满子树的高度也是2。而根节点的 NPL 值为3,也可以理解为以它为根的极大满子树的高度也是3。

至此,我们已经建立了 NPL 值这样一个重要的指标。以这个指标作为基准,我们就来度量堆结构的倾斜性。

5. 左倾性

在这里插入图片描述
是的,借助 NPL 这个指标,我们就可以简明地定义什么叫做左倾。也就是说对于任何一个节点 x, 如果在 NPL 的意义上,它的左孩子不小于它的右孩子,我们就称之为左倾。 如果在一个堆中,任何内部节点都是左倾的,我们就称这个堆为左倾堆,或者左式堆、左撇子堆。

当然根据 NPL 的定义,既然每个节点 x 的 NPL 值都是在它的孩子中间取一个小者,再累计上 1 个单位,于是很自然地在这个递推式中,我们就只需考虑每个节点的右孩子,而忽略它的左孩子。

不难确认,所谓的"左倾性"与我们此前的堆序性是彼此相容而不矛盾的。

既然左倾性是必须处处满足的,所以我们也自然地可以推知,任何一个左式堆的任何一个子堆必定依然是左式堆。

不难理解,作为左式堆,它更倾向于将更多的节点分布于左侧的分支,正像我们最初所希望的那样。然而需要指出的是,这只是一个大致的倾向,事实情况未必严格如此。

可以进一步来思考这样两个问题:

  1. 也就是在左式堆中,是否左子堆的规模总是大于右子堆?
  2. 另外左子堆的高度是否也必然总是大于右子堆?

在这里我们也给出了一个左式堆的具体实例(上图),而刚才两个问题答案也就藏在这个实例当中。

6. 左展右敛

在这里插入图片描述

实际上对于左式堆而言,左子堆和右子堆在规模和高度上的差异并不是那么重要,真正重要的是全堆的右侧链。

在以 x 为根的任何一个子堆中,从这个节点出发,一路向右,不断前行所确立的那个分支就称作为右侧链。

特别地,对于全树根节点而言,它的右侧链的终点必然是全堆中深度最小的一个外部节点。

比如在上图中,相对于根节点 r,它所对应的右侧链的终点应该是这个外部节点。

刚刚介绍过,在左式堆中 r 的 NPL 值必然是在它的右孩子基础上累进一个单位。而它的右孩子也必然是在它的右孩子基础上累进一个单位。

如此递推,可以得知所谓 NPL(r)值必然是在这个终端节点, NPL 值也就是0的基础上不断累进而得。因此,如果 r 的 NPL 值为 d,则不仅意味着这个外部节点的深度也是 d,而且更重要地,按照我刚才的结论,必然存在一棵以 r 为根的高度为 d 的极大满子树。这一点对于左式堆来说至关重要,事实上这就意味着在左式堆中应该包含足够多个节点。

因为即便我们只记录这棵满子树,也至少应该有 2 d + 1 − 1 2^{d + 1} - 1 2d+11 个节点。当然,其中也至少包含 2 d − 1 2^d - 1 2d1个内部节点。没错,如果根节点的 NPL 值为 d,那么其中就至少含有 2 d 2^d 2d个节点。

反过来,如果将左式堆的规模固定为 n,那么右侧链的长度 d 也就至多不过 log n。没错,右侧链的长度至多为 log(n)。这难道不正是我们最初的设计目标吗? 因此进一步地,如果我们所设计的堆合并算法的确也能将操作的范围限定在右侧链,那么相关算法的复杂度就很自然地也同样可以控制在log(n)的范围。那么这样的算法到底是如何运转的呢?

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

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

相关文章

洛谷刷题(4)

P1089 [NOIP2004 提高组] 津津的储蓄计划 题目描述 津津的零花钱一直都是自己管理。每个月的月初妈妈给津津 300 元钱,津津会预算这个月的花销,并且总能做到实际花销和预算的相同。 为了让津津学习如何储蓄,妈妈提出,津津可以随…

UE5打包iOS运行查看Crash日志

1、查看Crash 1、通过xCode打开设备 2、选择APP打开最近的日志 3、选择崩溃时间点对应的日志 4、选择对应的工程打开 5、就能看到对应的Crash日志 2、为了防止Crash写代码需要注意 1、UObject在Remov

sqlsugar 不映射字段,sqlsugar 忽略字段。sqlsugar 字段改名。

sqlsugar 不映射字段,sqlsugar 忽略字段。 利用特性SugarColumn,将IsIgnore设置为True即可! [SugarColumn(IsIgnore true)]//sqlsugar 忽略字段 public decimal MaxTemp { get; set; } 这样Sqlsugar 增删改查数据库的时候自动跳过该字段&…

Nextjs(App Router) 开发记录

最近业余在开发一款智能助理产品,记录开发过程中的一些问题以备忘,也是帮其他人防坑。 主要技术栈 本项目采用了前沿的技术栈来构建一个高性能且可维护的应用。选择了 Nx 作为构建管理和单一代码库解决方案,通过模块化和插件系统来扩展和优…

论文学习—Efficient Multi-label Classification with Many Labels

论文学习:Efficient Multi-label Classification with Many Labels 摘要2. 多标签分类相关工作2.1 Label Transformation1. **降维(Dimensionality Reduction)**2. **回归模型(Regression Model)**3. **逆变换&#xf…

【Python机器学习】NLP词中的数学——词袋

我们已经收集了一些词/词条,对这些词进行计数,并将它们归并成词干或者词元,接下来就可以做更多的事情。分析词对一些简单的任务有用,例如得到词用法的一些统计信息,或者进行关键词检索。但如果我们想知道哪些词对于某篇…

oracle备份策略

一、RMAN热备份 1、查看是否开启归档 archive log list;归档未开启 归档开启方法 – 关闭数据库,重启至mount模式 shutdown immediate startup mount– 开启归档模式 alter database archivelog;– 打开数据库 alter database open;– 检查归档模式是否打开 a…

微信小程序for循环怎么用

微信for简单使用&#xff1a; <view> <ul> <li wx:for"{{xiaoke}}" wx:key"index"> <a href"{{item.path}}">{{item.name}}</a> </li> </ul> </view> 其中" xiaoke " 是数组名称&am…

利用深度学习技术来实现街景图像的语义分割(街景图像语义分割)

本项目致力于利用深度学习技术来实现街景图像的语义分割。通过精确地识别和分类图像中的每个像素&#xff0c;该技术能够自动划分出街道、人行道、车辆、行人等各种不同的物体类别。这在智能交通系统、自动驾驶、城市规划等领域有着广泛的应用前景。 技术实现 深度学习模型&am…

zsh: command not found: brew(M系列芯片)

利用官网的命令安装完brew /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"查看版本&#xff0c;提示找不到命令 % brew -v zsh: command not found: brew解决方法&#xff0c;在终端中执行以下命令&#xff0c…

git仓库删除某个历史提交

目录 问题情况1情况2 问题 如果我们在开发过程中&#xff0c;存在一些验证性的提交或者失误性的提交&#xff0c;那么这些提交我们不想要了&#xff0c;怎么办&#xff1f; 情况1 如果是想要删除某个commitid之后的所有提交 那么git reset 可以满足你 git reset --hard 你要…

Keilv5 逻辑分析仪的使用

声明&#xff1a;基于视频【事件驱动型编程和 QP/C 框架】所做的笔记 Keilv5逻辑分析仪是Keil MDK集成开发环境&#xff08;IDE&#xff09;中的一个工具&#xff0c;用于帮助开发人员进行嵌入式系统的调试和分析。 它的作用主要有&#xff1a; 监测信号&#xff1a;Keilv5逻…

【Qt | 音视频学习路线(高薪路线 AI)】

Answer 学习Qt音视频开发的学习路线可以分为以下几个阶段: 1. 基础知识准备 C++编程基础:Qt主要使用C++,因此需要有扎实的C++编程基础。计算机图形学基础:了解基本的图形学概念,如图像处理、渲染等。音视频基础:了解音视频的基本概念,如编码、解码、格式等。2. 学习Qt…

QGIS制图流程

在之前我们推送了QGIS的软件安装、插件安装、数据导入等基础操作&#xff0c;今天我们介绍一下QGIS的制图功能。QGIS的制图与ArcGIS Pro存在一定的区别&#xff0c;但是思路上相似。我们教程内容主要是参考QGIS官方文档&#xff1a; https://docs.qgis.org/3.34/en/docs/user_…

JUC并发编程-volatile

目录 1. volatile的两大特性 可见性&#xff08;Visibility&#xff09; 有序性&#xff08;Ordering&#xff09; 2. 四大屏障 3. 读写屏障插入策略 happens-before与volatile变量规则&#xff1a; 注意事项 4. 原子性 5. 禁重排 6.使用场景 传统的单例模式实现如下…

Apache Doris 使用 CBO 和 RBO 结合的优化策略

Apache Doris 在查询优化方面通过结合 RBO 和 CBO,实现了对简单和复杂查询的高效优化。RBO 负责处理常量折叠、子查询改写和谓词下推等基础优化操作,而 CBO 则在 Join Reorder 等复杂场景中发挥作用。这种结合策略使得 Apache Doris 能够在面对各种查询场景时,既能保证优化过…

大数据技术之 Flume概述、安装(1)

目录 Flume 概述 Flume 定义 为什么选用 Flume Flume 基础架构 Agent Source Sink Channel Event Flume 安装 Flume 安装部署 安装地址 安装部署 Flume 概述 Flume 定义 Flume 是 Cloudera 提供的一个高可用的、高可靠的、分布式的海量日志采集、聚合和传输的系统。Flume…

Clickhouse集群化(六)clickhosue-operator学习

1. Custom Resource元素 apiVersion: "clickhouse.altinity.com/v1" kind: "ClickHouseInstallation" metadata:name: "clickhouse-installation-test" 这是clickhouse operator自定义的资源ClickHouseInstallation 1.1. .spec.defaults spe…

Webbench1.5安装使用Ubuntu

1、安装依赖包 sudo apt-get update sudo apt-get install libtirpc-dev2、安装Webbench1.5 参考https://github.com/baiguo/webbench-1.5 # 可能需要root权限&#xff0c;我是切换到root用户才安装成功 wget http://home.tiscali.cz/~cz210552/distfiles/webbench-1.5.tar.…

安卓系统 XBL阶段详解

在安卓系统的启动流程中&#xff0c;XBL&#xff08;eXtensible Boot Loader 或 Secondary Bootloader&#xff09;是一个关键阶段&#xff0c;特别是在使用QualComm&#xff08;高通&#xff09;等SOC&#xff08;System on Chip&#xff09;的设备上。以下是对XBL阶段的详细解…