(b1)完全二叉堆:结构

官官方信息技术老师·13 页·深入(追求细节与边界)·1 次浏览·38 天前
优先级队列完全二叉树数组映射

完全二叉堆:结构

看完你能画出任一数组的堆形态,并说清数组为何能存树

按 空格/→ 演示下一步

问一句
1 / 13 页

全部页面点击任意一页,跳回舞台从这页播放

优先级队列完全二叉树数组映射

完全二叉堆:结构

看完你能画出任一数组的堆形态,并说清数组为何能存树

1第 1 页 · 完全二叉堆:结构

为什么需要堆

前页看到:有序数组取最值快但插入慢,无序数组插入快但取最值慢。鱼与熊掌都想占,就需要一种新的组织方式——让「找最值」和「插入」都快,这就是堆要解决的问题。

两难困境
有序数组插入 O(n),无序数组取最值 O(n),总有一头慢
妥协的艺术
不追求完全有序,只维护「父不大于子」的局部秩序
堆的承诺
最值总在顶部,取最值 O(1),插入/删除 O(log n)
完全二叉树
用形状保证层次只有 log n 层,操作才快得起来
公司组织架构对应 →堆结构

总裁在最顶层对应最值总在堆顶,逐层下管对应父子偏序

2第 2 页 · 为什么需要堆

什么是完全二叉树

上一页说了堆要支持「随时找最值」,这就要求树必须长得规整、不能歪。先看规整的标准:想象排队上电梯,一层站满才开下一层,这就是完全二叉树的画法。

逐层填满
除最后一层外,每层节点必须排满,不留空位
最后一层靠左
最底层节点从左到右连续排列,右侧可以缺
高度有保障
n 个节点树高约 log₂n,操作不退化为链
电影院逐排入座对应 →完全二叉树

前排坐满才坐下一排,末排从左往右依次坐

h=⌊log⁡2n⌋h=\lfloor\log_2 n\rfloor
3第 3 页 · 什么是完全二叉树

完全 vs 满满 vs 一般

从「二叉树家族」出发,向下看三类成员各自的形状约束:满树最严,完全树次之,一般树最自由。

图解渲染中…
c4:如4层树最后一层恰好填满,节点数2^h-1c5:最后一层节点从左到右连续,右侧可空c6:既不满也不完全,堆不采用
4第 4 页 · 完全 vs 满满 vs 一般

结构性:堆的形

上一页我们区分了完全二叉树和满二叉树。堆要的正是完全二叉树这个形状:除最底层外每层都填满,最底层从左往右排。这个约束不是为了好看,它直接决定了堆怎么存、怎么插。

除末层外填满
上面每一层的节点数都达到该层最大值,不留空洞
末层左对齐
最底层节点必须从最左位置连续排布,右边可空
不许跳位
中间有空缺、只填右侧的形状都不合格,不是堆
一排书架从左往右摆对应 →完全二叉堆的结构

书架上层必须摆满,最下层也只能从左端连续摆

5第 5 页 · 结构性:堆的形

父子的下标关系

把堆的树形画成数组:先看节点 0 的两个孩子落在哪,再看任意节点 i 的通用公式。

图解渲染中…
v0:树按层序摊平成向量,下标即位置
6第 6 页 · 父子的下标关系

向量中的堆

python

用一小段代码验证:堆不用指针,父子关系全靠下标算出来。

代码高亮加载中…

下标 i 从 0 起:(i-1)//2 定位父,2i+1、2i+2 定位左右孩子;层序铺进向量后,整棵树形不用任何指针即可还原。

7第 7 页 · 向量中的堆

形具神备

前页我们看到堆在数组里排列成完全二叉树。但光有树形还不够——若父节点比子节点小,取最值就失效。形已具备,还需神的约束:堆序性。

结构性给形
完全二叉树形态决定存储位置与父子下标关系
堆序性给神
任一父节点键值不小于(或不大于)其子节点
缺一不成堆
无结构则下标失效,无堆序则无法快速取最值
公司组织架构对应 →完全二叉堆

组织结构图对应结构性,职级上下级对应堆序性

8第 8 页 · 形具神备

堆序性:大小的次序

上一页我们确定了堆的「形」:一棵装在向量里的完全二叉树。但光有形状还不够——树里各处该放什么值?堆序性给出约束:只规定父子间的大小关系,不追求全排序。

不大于父亲
每个节点 ≤ 其父节点,自下而上逐层不减
兄弟无序
同一父亲的两个孩子之间没有大小约定
路径偏序
沿任一自上而下的路径,值单调不增
根即最大
由传递性,堆顶即全局最大值,O(1) 读取
公司汇报层级对应 →堆序性

职级只约束上下级:上级不低于直接下属,同级之间互不比较

∀v:  key(v)≤key(parent(v))  ⇒  key(root)=max⁡\forall v:\; key(v) \le key(parent(v))\;\Rightarrow\; key(root)=\max
9第 9 页 · 堆序性:大小的次序

堆序 ≠ 全序

看箭头方向:每个孩子都指向小于自己的父亲,但兄弟之间没有箭头约定。

图解渲染中…
b1:40 与 60 是兄弟,大小任凭,不必左小于右c2:45 与叔叔 70 也无从比较,偏序只沿父链
10第 10 页 · 堆序 ≠ 全序

自测:这棵树是堆吗

点击作答

一棵完全二叉树,每个结点都大于等于它的子结点。它是堆吗?

11第 11 页 · 自测:这棵树是堆吗

小结:形与神

  • ✓完全二叉树保证树矮而整齐,这是堆的『形』
  • ✓堆序性保证父子大小关系,这是堆的『神』
  • ✓形与神合一,堆才成立,缺一不可
  • ✓数组下标即可描述父子关系,无需指针
  • ✓堆只约束局部次序,不是全序结构
延伸主题:堆的核心操作:上滤与下滤建堆:自上而下的插入法Floyd 建堆:O(n) 的批量构建
12第 12 页 · 小结:形与神

课后思考

先自己想几分钟,再对照参考答案,看思路是否一致。

1为什么堆必须挑完全二叉树这个形状,而不是一般二叉树或满二叉树?

参考答案从两头想:满二叉树太苛刻,节点数必须恰好 2^h−1,装不下任意 n;一般二叉树会歪,无法紧凑排列。完全二叉树层次整齐又能容纳任意 n,还能无缝映射进数组。

2如果用链表而不是数组来存这个完全二叉树,父子的下标关系还能用吗?

参考答案下标关系本质是数组按层编号带来的,换链表就没了下标。链表里只能存指针,需额外字段,且失去按 i/2、2i 直接定位的 O(1) 便利。

3把同样的结构规则搬到三叉树上,下标关系会变成什么样,还划算吗?

参考答案d 叉堆:父 i 的孩子是 d·i+1 … d·i+d,父是 ⌊(i−1)/d⌋。树更矮,但下沉时比较变多。d 大时未必划算,要按场景权衡。

13第 13 页 · 课后思考
(b1)完全二叉堆:结构 · 知识图解