相关笔记

19.1 不相交集合操作 | 19.2 不相交集合的链表表示 | 19.3 不相交集合森林 | 势能方法 | 聚合分析 | 第19章_用于不相交集合的数据结构-章节汇总

概览

本节是本章最数学化的一节,使用摊还分析的势能方法严格证明按秩合并+路径压缩的 时间上界。核心知识点包括:

  • 快速增长函数 :一个随层级 极速增长的函数族,用于定义节点的”级别”
  • 反阿克曼函数 的”逆函数”,增长极其缓慢,对所有实际输入
  • 势能函数设计:基于 level 和 iter 两个辅助函数,为每个节点分摊势能
  • 定理19.14 次操作(含 次 MAKE-SET)的总时间为

知识结构总览

graph TB
    A["19.4 按秩合并与路径压缩的分析"] --> B["快速增长函数 A_k(j)"]
    A --> C["反阿克曼函数 α(n)"]
    A --> D["秩的性质"]
    A --> E["势能函数与摊还分析"]
    A --> F["最终定理"]

    B --> B1["A_0(j) = j + 1"]
    B --> B2["A_1(j) = 2j + 1"]
    B --> B3["A_2(j) = 2^(j+1) · (j+1) - 1"]
    B --> B4["A_k(j) = A_(k-1)^(j+1)(j)<br/>对j+1次迭代"]
    B --> B5["A_4(1) >> 10^80<br/>远超宇宙原子数"]

    C --> C1["α(n) = min{k : A_k(1) ≥ n}"]
    C --> C2["α(n) ≤ 4 对所有实际n"]
    C --> C3["实际等价于常数"]

    D --> D1["引理19.4:秩的性质"]
    D --> D2["推论19.5:路径上秩严格递增"]
    D --> D3["引理19.6:rank ≤ ⌊lg n⌋"]
    D --> D4["引理19.7:rank为r的节点至多n/2^r个"]

    E --> E1["level(x) 的定义与性质"]
    E --> E2["iter(x) 的定义与性质"]
    E --> E3["节点势能 φ(x)"]
    E --> E4["引理19.10:势能单调不增"]
    E --> E5["引理19.11:MAKE-SET O(1)"]
    E --> E6["引理19.12:LINK O(α(n))"]
    E --> E7["引理19.13:FIND-SET O(α(n))"]

    F --> F1["定理19.14:O(m α(n))"]
    F --> F2["习题19.4-7:改进上界 O(m α'(n))"]

核心思想

本节的目标是证明:使用按秩合并和路径压缩, 次不相交集合操作(其中 次为 MAKE-SET)的总运行时间为 。证明使用势能方法(potential method),这是 势能方法 中的核心工具。

证明的整体策略分为四个层次:

  1. 定义快速增长函数 及其逆函数 :建立”级别”的数学基础
  2. 证明秩的基本性质:为后续分析提供不等式工具
  3. 设计势能函数:基于 level 和 iter 两个辅助函数,为每个节点分配势能
  4. 分析每种操作的摊还代价:证明 MAKE-SET 为 ,LINK 和 FIND-SET 均为

快速增长函数 的定义

对于整数 ,函数 的递归定义为:

其中 表示将函数 迭代作用 次于 ,即:

参数 称为函数 层级(level)。

前几层的计算与闭式表达

第0层:由定义直接可得

第1层。先求 的闭式。 每次作用将参数加 1,因此迭代 次后:

由此:

引理19.2

对任意整数

第2层。先求 的闭式。 每次作用将参数做线性变换 ,因此:

用归纳法验证:基础 时,。归纳步骤:

由此:

引理19.3

对任意整数

第3层 的增长速度已经是指数级的, 的增长速度远超指数。教材给出:

第4层

是一个远超可观测宇宙中原子数(约 )的巨大数字。

各层 的值汇总

增长速度
02线性
13线性
27指数
32047超指数
4不可想象

反阿克曼函数

换言之, 是使得 的最小层级

根据 的值:

的范围
02
13
27
32047
4

对所有实际可能的 值成立。 是一个远超宇宙中原子数(约 )的巨大数字,因此任何 conceivable 的应用中 都不超过 4。在实际编程中,并查集操作可以安全地视为 常数时间。

秩的性质

引理19.4(秩的性质)

对于所有节点 ,有 ,且若 不是根),则严格不等。 初始为 0,随时间递增直到 ,此后 不再改变。 随时间单调递增。

推论19.5

从任意节点向上到根的简单路径上,节点秩严格递增。

引理19.6(秩上界

每个节点的秩最多为

引理19.7(rank 为 的节点个数上界)

个节点的森林中,至多有 个 rank 为 的节点。

势能函数设计

为了使用势能方法证明 的上界,教材设计了一个精巧的势能函数。假设操作序列已被转换为 MAKE-SET、LINK、FIND-SET 序列(每个 UNION 被替换为两次 FIND-SET 加一次 LINK)。

辅助函数 level()(仅对非根节点且 定义):

即 level() 是满足 作用于 后不超过 的最大层级

level() 的性质

  1. 下界(由引理19.4,,而 rank 为整数),因此 level()

  2. 上界(因为 ),因此 level()

综上,

  1. 单调性:对于给定的非根节点 ,level() 随时间单调递增。这是因为 不变( 不是根),而 单调递增(引理19.4)。

辅助函数 iter()(仅对非根节点且 定义):

即 iter() 是在层级 level() 上,将 迭代作用于 后仍不超过 的最大迭代次数。

iter() 的性质

  1. 下界(由 level 的定义),因此 iter()

  2. 上界:由 level 的定义,。而 ,所以 ,即 iter()

综上,

  1. 单调性:只要 level() 不变,iter() 随时间单调递增或不变(因为 单调递增)。

节点势能

引理19.8(势能有界性)

对每个节点 和所有操作计数 ,有

摊还代价分析

引理19.10(势能变化)

是非根节点,第 次操作是 LINK 或 FIND-SET。则 。进一步,若 且 level() 或 iter() 因第 次操作而变化,则

引理19.11(MAKE-SET 摊还代价)

每次 MAKE-SET 的摊还代价为

引理19.12(LINK 摊还代价)

每次 LINK 的摊还代价为

引理19.13(FIND-SET 摊还代价)

每次 FIND-SET 的摊还代价为

最终定理

定理19.14

使用按秩合并和路径压缩, 次 MAKE-SET、UNION 和 FIND-SET 操作(其中 次为 MAKE-SET)可以在 时间内完成。


补充理解与拓展

补充:α(n) 增长极其缓慢

来源: 教材第19.4节,pp. 533-534

是所有实用数据结构分析中出现的增长最慢的非常数函数。具体来说:

,远超宇宙中可观测原子数(约 )。因此 对所有”天文数字”级别的 都成立。在实际编程中,并查集操作可以安全地视为 常数时间。

补充:Ackermann 函数的历史

来源: Wilhelm Ackermann, “Zum Hilbertschen Aufbau der reellen Zahlen”, Mathematische Annalen, 1928

Ackermann 函数是最早被发现的不是原始递归的递归函数之一,由数学家 Wilhelm Ackermann 于 1928 年提出。原始的 Ackermann 函数 是一个双参数函数,增长速度极快——远超指数函数和阶乘函数。

教材中使用的快速增长函数 是 Ackermann 函数的一个变体,经过调整以便于并查集的分析。两者本质相同:都通过函数迭代来定义更高层级的增长速度。 可以看作是将 Ackermann 函数”展平”为单参数层级 的版本。

补充:Tarjan 1975 的原始证明

来源: Robert E. Tarjan, “Efficiency of a Good But Not Linear Set Union Algorithm”, Journal of the ACM, 22(2), 1975, pp. 215-225

Tarjan 在 1975 年首次证明了按秩合并+路径压缩的 上界。原始证明使用了不同的势能函数和 level 定义,但核心思想相同:利用快速增长函数的逆函数来”分级”节点,使得每个 level 的摊还代价有界。

Tarjan 的原始证明中使用的函数定义与教材略有不同(使用的是 Ackermann 函数的另一个变体),但最终得到的上界在量级上是相同的。这篇论文是摊还分析在数据结构中应用的经典范例之一。

补充:Fredman & Saks 的下界证明

来源: M. Fredman and M. Saks, “The Cell Probe Complexity of Dynamic Data Structures”, Proceedings of the 21st Annual ACM Symposium on Theory of Computing (STOC), 1989, pp. 345-354

Fredman 和 Saks 在 1989 年证明了并查集在 cell probe 模型下的下界:任何实现不相交集合 MAINTAINABLE 操作的动态数据结构,每次操作至少需要 的时间。这意味着 Tarjan 的 上界在 cell probe 模型下是最优的——不可能有渐近更快的算法。

Cell probe 模型是一种非常强的计算模型,它只计算算法读取或写入内存单元的次数,不考虑计算开销。因此, 的下界是非常强的结论。

补充:实际意义——α(n) = O(1)

来源: 教材第19.3节,p. 531

教材明确指出:“在任何可以想象的不相交集合数据结构的应用中,。“这意味着:

  • 在实际工程中,并查集的每次操作可以视为常数时间
  • Kruskal 最小生成树算法中使用并查集时,总时间由排序步骤的 主导,并查集部分为
  • 竞赛编程中,并查集被视为”近乎常数时间”的数据结构,与哈希表同级
  • 即使 (远超任何实际输入), 仍然只有 4

易混淆点与辨析

误区:A_k(j) 的定义中 A_0(j) = 2j

错误理解: ,这是第3版的定义。 正确理解: 在第4版中,)。这与第3版的定义不同。 辨析: 第3版使用 。第4版修改了定义使得 (而非第3版的 ),分析结构更加简洁。两版最终得到的 在量级上相同,但具体数值有差异。阅读时务必确认使用的是哪个版本的教材。

误区:level(x) 沿查找路径单调递增

错误理解: 因为节点秩沿路径严格递增,所以 level 也一定沿路径单调递增。 正确理解: level() 不一定沿查找路径单调递增。习题19.4-5要求给出反例。 辨析: level() 依赖于 之间的差距。虽然 沿路径递增,但 也沿路径递增,因此 之间的相对关系可能非常复杂。具体来说,当路径上两个相邻节点的 rank 差距恰好使得低 rank 节点的 level 较高时,就会出现 level 不单调的情况。

误区:势能函数中的 α(n) 可以替换为任意常数

错误理解: 既然 ,势能函数中直接用 4 替代 就行了。 正确理解: 在理论分析中, 不能简单替换为常数。势能函数的设计依赖于 的定义(即 ),这个性质在证明 level() 时被使用。 辨析: 虽然在实际中 等价于常数,但在数学证明中, 的精确定义和性质是证明正确性的基础。如果直接替换为 4,level() 这个界在 时可能不成立,证明就会失效。当然,对于任何实际输入,,所以替换为 4 在实践中是安全的。


习题精选

习题概览

题号来源核心考点难度
19.4-1教材习题证明引理19.4(秩的性质)⭐⭐
19.4-2教材习题证明 rank ≤ ⌊lg n⌋⭐⭐
19.4-3教材习题存储 rank 需要的位数
19.4-4教材习题仅按秩合并的 O(m lg n) 证明⭐⭐
19.4-5教材习题level 是否沿路径单调递增⭐⭐⭐
19.4-7教材习题α’(n) 的改进上界⭐⭐⭐

题1:19.4-1 证明引理19.4

题目

证明引理19.4:对于所有节点 ,有 ,且若 则严格不等。 初始为 0,随时间递增直到 ,此后不再改变。 随时间单调递增。

解题思路提示

对操作次数进行归纳,分 MAKE-SET、LINK、FIND-SET 三种情况讨论。注意 LINK 只操作根节点,FIND-SET 只改变 parent 指针不改变 rank。

题2:19.4-2 证明 rank ≤ ⌊lg n⌋

题目

证明每个节点的秩最多为

解题思路提示

核心思路是证明”rank 为 r 的根至少是 2^r 棵初始树合并而来的”。这通过归纳法完成,利用了 LINK 只在两个 rank 相等的根合并时才增加 rank 这一事实。

题3:19.4-3 存储 rank 需要的位数

题目

根据练习19.4-2,存储 需要多少位?

解题思路提示

rank 的最大值是 floor(lg n),因此需要表示 0 到 floor(lg n) 共 floor(lg n)+1 个值。对其取对数即得所需位数。

题4:19.4-4 仅按秩合并的 O(m lg n) 证明

题目

利用习题19.4-2,给出一个简单的证明:仅使用按秩合并(不使用路径压缩)的不相交集合森林上的操作在 时间内运行。

解题思路提示

关键在于:按秩合并保证树高为 O(lg n)(因为 rank 不超过 floor(lg n) 且路径上秩严格递增),因此每次 FIND-SET 为 O(lg n)。没有路径压缩时,这个界不会被打破。

题5:19.4-5 level 是否沿路径单调递增

题目

Dante 教授认为,因为节点秩沿到根的简单路径严格递增,所以节点 level 也必须沿路径单调递增。换言之,如果 不是根,则 level() level()。教授正确吗?

解题思路提示

要证明”不单调”,只需构造一个具体的反例。关键是找到 rank 差距”恰到好处”的相邻节点对,使得低 rank 节点的 level 反而更高。从 A_k 的定义出发,选择使 A_1 作用于 x.rank 不超过 x.p.rank 但 A_2 作用于 x.rank 超过 x.p.rank,同时 A_0 作用于 x.p.rank 不超过其父节点 rank 但 A_1 作用于 x.p.rank 超过其父节点 rank 的数值。

题6:19.4-7 α’(n) 的改进上界

题目

考虑函数 。证明 对所有实际 成立,并利用习题19.4-2,说明如何修改势能函数的论证来证明:使用按秩合并和路径压缩, 次 MAKE-SET、UNION 和 FIND-SET 操作(其中 次为 MAKE-SET)可以在 时间内完成。

解题思路提示

核心思路是将分析中的 n 替换为 lg(n+1),利用 rank 不超过 floor(lg n) 这一更紧的界。由于 rank 的最大值本身就是 O(lg n),用 lg(n+1) 作为 level 的上界更加”紧凑”,从而得到更小的 alpha’(n)。


视频学习指南

视频资源

资源链接对应内容备注
MIT 6.046 Lecture 11YouTube并查集的摊还分析含 α(n) 的直观解释
Erik Demaine - Advanced Data StructuresMIT OCW不相交集合的完整分析更深入的数学推导
Tarjan 1975 原论文ACM DL原始 O(mα(n)) 证明历史文献,了解原始方法

教材原文(中文翻译)

教材原文

来源: Introduction to Algorithms, 4th Edition, Section 19.4, pp. 532-540 译者: 殷建平、徐云、王刚、刘晓光、苏明、邹恒明、王宏志

按秩合并与路径压缩的分析

如第19.3节所述,结合按秩合并和路径压缩的启发式策略在 个元素上执行 次不相交集合操作的运行时间为 。在本节中,我们将探讨函数 以了解它增长得有多慢。然后我们将使用摊还分析的势能方法来分析运行时间。

一个非常快速增长的函数及其非常缓慢增长的逆函数

对于整数 ,我们定义函数 为:)。我们称参数 为函数 的层级。

函数 严格递增。为了了解这个函数增长得有多快,我们首先获得 的闭式表达式。

引理19.2:对任意整数

引理19.3:对任意整数

现在我们可以通过简单地考察 时的 来了解 增长得有多快。由 的定义和上述引理,我们有 。我们还有 ,以及 ,后者是可观测宇宙中估计的原子数。

我们定义函数 为整数)的逆为 。换言之, 是使 至少为 的最低层级 。只有当 大到”天文数字”这个词都低估了它(大于 ,一个巨大的数字)时,,因此 对所有实际目的成立。

秩的性质

引理19.4:对于所有节点 ,且若 不是根),则严格不等。 初始为 0,随时间递增直到 ,此后 不再改变。 随时间单调递增。

推论19.5:从任意节点向上到根的简单路径上,节点秩严格递增。

引理19.6:每个节点的秩最多为

引理19.6提供了一个较弱的秩的界。事实上,每个节点的秩最多为 (见练习19.4-2)。然而,引理19.6的较松界对我们的目的已经足够。

证明时间界

为了证明 的时间界,我们将使用第16.3节的摊还分析势能方法。在执行摊还分析时,假设我们调用 LINK 操作而非 UNION 操作会比较方便。也就是说,由于 LINK 过程的参数是指向两个根的指针,我们表现得好像分别执行了相应的 FIND-SET 操作。

势能函数

我们使用的势能函数在第 次操作后为不相交集合森林中的每个节点 分配一个势能 。对于第 次操作后整个森林的势能 ,对所有节点的势能求和。因为在第一次操作之前森林为空,求和在一个空集上进行,所以 。任何势能 都不会为负。

的值取决于 在第 次操作后是否为树根。如果是,或者 ,则

现在假设在第 次操作后 不是根且 。我们需要在定义 之前先定义 上的两个辅助函数。首先定义 level() 。即 level() 是使得 作用于 的秩后不超过 的父节点秩的最大层级

第二个辅助函数在 时应用:iter() 。即 iter() 是在层级 level() 上,将 迭代作用于 的秩后仍不超过 的父节点秩的最大迭代次数。

有了这些辅助函数,我们准备定义节点 次操作后的势能:(当 不是根且 时)。

势能变化与操作的摊还代价

引理19.10:设 是非根节点,假设第 次操作是 LINK 或 FIND-SET。则在第 次操作后,。进一步,若 且 level() 或 iter() 因第 次操作而变化,则

引理19.11:每次 MAKE-SET 操作的摊还代价为

引理19.12:每次 LINK 操作的摊还代价为

引理19.13:每次 FIND-SET 操作的摊还代价为

定理19.14:使用按秩合并和路径压缩, 次 MAKE-SET、UNION 和 FIND-SET 操作(其中 次为 MAKE-SET)可以在 时间内完成。

证明:直接由引理19.7、19.11、19.12和19.13得出。【总摊还代价 = 各操作摊还代价之和,初始势能=0 且势能非负】


参见Wiki: 按秩合并 — 按秩合并的定义与性质 | 路径压缩 — 路径压缩的定义与效果 | 反阿克曼函数 — 复杂度分析中的关键函数 α(n) | 按秩合并与路径压缩定理

第19章-用于不相交集合的数据结构 #学习/算法导论/不相交集合/按秩合并与路径压缩的分析