算法认知实验室 · 第八章

图与并查集|Unit 13|最小生成树与 Kruskal

学习状态自动保存
LeetCode 1584 · Min Cost to Connect All Points

最便宜的边,不一定能加入。
先问:它会不会形成环?

把每对点之间的曼哈顿距离视为边权,按照权重从小到大审查边。并查集负责判断两端是否已经连通;只有连接两个不同分量的边,才会进入最小生成树。

13
章节终局主线

带权完全图 → 边排序 → 并查集判环 → Kruskal → 最小生成树

已恢复上次学习现场阶段、动画、代码和迁移进度均已恢复。
1584. 连接所有点的最小费用

给定二维平面上若干互不相同的整数坐标点。连接两点的费用等于它们的曼哈顿距离:

|xᵢ - xⱼ| + |yᵢ - yⱼ|

请选择一些连接,使任意两点之间恰好存在一条简单路径,并返回使全部点连通的最小总费用。

示例 1 输入:points = [[0,0],[2,2],[3,10],[5,2],[7,0]] 输出:20 示例 2 输入:points = [[3,12],[-2,5],[-4,1]] 输出:18
  • 1 ≤ points.length ≤ 1000
  • 坐标范围为 -10⁶ 到 10⁶
  • 所有点坐标两两不同
打开 LeetCode 官方题目 ↗
本单元要建立的状态契约
候选边按非递减权重到达。

accepted_edges 始终无环;并查集精确表示当前已接受边形成的连通分量。

只有 root(u) ≠ root(v) 时,候选边才能连接两个分量并被接受。
MST

连接全部节点、无环、边数为 n−1,且总权重最小的生成树。

Kruskal

按边权从小到大审查,用并查集拒绝会形成环的边。

曼哈顿距离

横向距离与纵向距离绝对值之和,不是欧氏直线距离。

停止条件

成功接受 n−1 条边后,已经得到一棵覆盖全部节点的树。

Stage 1 · Model

先把坐标点编译成带权完全图

输入只给了点,没有直接给边。任何两点都可以连接,因此需要枚举每个点对,并用曼哈顿距离生成边权。

01

节点是什么?

数组下标 0..n−1 是稳定节点身份;坐标只是节点属性。即使两个点到其他点的距离相同,它们仍是不同节点。

边是什么?

任意两个不同点 i、j 之间都存在一条无向候选边,因此 n 个点会生成 n(n−1)/2 条边。

权重是什么?

cost(i,j)=|xᵢ−xⱼ|+|yᵢ−yⱼ|。本题必须使用曼哈顿距离。

坐标点与全部候选边点击边表中的行观察距离计算
点对编译器10 条边
选择一条边,观察 |Δx| + |Δy|。
读图检查:5 个点构成的完全无向图有多少条候选边?
Stage 2 · Greedy Experience

从最便宜的边开始,但每次都要做“成环审查”

排序只决定候选边到达顺序;并查集决定该边是否有资格进入结果。

02
Kruskal 逐边动画Step 0
排序后的候选边橙=审查,绿=接受,红=拒绝
当前边—
两端根—
已选边0 / 4
累计费用0
动画走到候选边 0—3(权重 7)时,为什么它即使很便宜也不能加入?
Stage 3 · State Contract

并查集不是附属工具,而是“当前森林”的压缩表示

图上已经接受的绿色边与 parent 森林必须表达同一个连通关系。

03
当前 parent / size随 Stage 2 动画同步
1. 最小候选
排序保证当前边是尚未审查边中权重最小者。
2. 跨分量
两端不同根,说明它连接两个独立树。
3. 保持无环
跨分量连接不会产生闭合路径。
4. n−1 条边
无环且覆盖 n 个点时得到一棵生成树。
循环不变量:在审查下一条边之前,accepted_edges 是某个最小生成树的可扩展前缀;并查集中的每个集合恰好对应当前森林中的一个连通分量。
为什么接受第 n−1 条边后可以立即停止,不必继续审查更重的边?
Stage 4 · Invariants & Errors

看起来合理的局部贪心,为什么会破坏全局结构?

选择一个错误实验,观察被破坏的是距离定义、无环契约、停止条件还是费用累计。

04

正确局部决策

在所有尚可扩展的边中,选择权重最小且连接不同分量的边。

错误局部决策

只看“这条边很短”,却不检查它是否重复连接已经连通的区域。

复杂度账本

完全图有 O(n²) 条边;显式生成并排序边的 Kruskal 实现约为 O(n² log n)。

为什么“每个点连接自己的最近邻”不能保证得到一棵最小生成树?
Stage 5 · Fade-out Code Linkage

先恢复功能槽位,再补关键表达式,最后独立完成

把完整 Kruskal 演示逐步撤掉:先从语义恢复代码骨架,再用真实轨迹校准状态,最后关闭脚手架。

05
关键槽位补全先选左侧槽位 → 再选右侧代码

左侧只描述“这一段代码负责什么”;右侧候选只显示代码,顺序每次新学习现场随机。

实时拼接后的代码骨架0 / 8
当前认知焦点Step 0

queue
不适用
visited
不适用
current
—
状态契约
—
代码 × 状态逐行联动Line 1
当前边 / 根
parent / size
accepted / rejected
answer / used

状态依赖问题|主工作区下方整行

当前候选边两端已经同根时,哪一组状态应该保持不变?

关键代码补全

功能槽位通过后,只保留 7 个决定正确性的表达式;这一步仍不要求从空白重写完整程序。

独立完成 #1584

脚手架已撤掉。请从函数入口独立恢复完整算法。
Stage 6 · Transfer

迁移到 LeetCode #1631:从“最小生成树”迁移到“首次连通阈值”

保留“边权升序 + 并查集”骨架,只改节点、边、停止条件和返回量。

06
1631. 最小体力消耗路径

给定高度矩阵,从左上角走到右下角。路径体力为沿途相邻格高度差绝对值的最大值,求可能路径中的最小体力。

输入:heights = [[1,2,2],[3,8,2],[5,3,5]] 输出:2
迁移焦点

#1584

  • 全部点连通
  • 累加接受边权
  • n−1 条边停止

#1631

  • 只关心起点与终点
  • 不累加边权
  • 首次同根立即返回当前 effort

第八章 Unit 13 Skill 卡

完成主问题和迁移问题的渐隐脚手架后,可完成本单元。

建图信号

坐标点对和网格邻接都能生成带权边。

Kruskal 信号

边权升序 + 并查集决定接受或拒绝。

停止信号

MST 看 n−1 条边;阈值题看目标节点首次同根。