相关笔记
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),这是 势能方法 中的核心工具。
证明的整体策略分为四个层次:
- 定义快速增长函数 及其逆函数 :建立”级别”的数学基础
- 证明秩的基本性质:为后续分析提供不等式工具
- 设计势能函数:基于 level 和 iter 两个辅助函数,为每个节点分配势能
- 分析每种操作的摊还代价:证明 MAKE-SET 为 ,LINK 和 FIND-SET 均为
快速增长函数 的定义
对于整数 ,函数 的递归定义为:
其中 表示将函数 迭代作用 次于 ,即:
参数 称为函数 的层级(level)。
前几层的计算与闭式表达
第0层:由定义直接可得
第1层:。先求 的闭式。 每次作用将参数加 1,因此迭代 次后:
由此:
引理19.2
对任意整数 ,。
第2层:。先求 的闭式。 每次作用将参数做线性变换 ,因此:
用归纳法验证:基础 时,。归纳步骤:。
由此:
引理19.3
对任意整数 ,。
第3层:。 的增长速度已经是指数级的, 的增长速度远超指数。教材给出:
第4层:
是一个远超可观测宇宙中原子数(约 )的巨大数字。
各层 的值汇总:
| 增长速度 | ||
|---|---|---|
| 0 | 2 | 线性 |
| 1 | 3 | 线性 |
| 2 | 7 | 指数 |
| 3 | 2047 | 超指数 |
| 4 | 不可想象 |
反阿克曼函数
换言之, 是使得 的最小层级 。
根据 的值:
| 的范围 | ||
|---|---|---|
| 0 | 2 | 当 |
| 1 | 3 | 当 |
| 2 | 7 | 当 |
| 3 | 2047 | 当 |
| 4 | 当 |
对所有实际可能的 值成立。 是一个远超宇宙中原子数(约 )的巨大数字,因此任何 conceivable 的应用中 都不超过 4。在实际编程中,并查集操作可以安全地视为 常数时间。
秩的性质
引理19.4(秩的性质)
对于所有节点 ,有 ,且若 ( 不是根),则严格不等。 初始为 0,随时间递增直到 ,此后 不再改变。 随时间单调递增。
证明
【对操作次数归纳:基础 MAKE-SET 满足,归纳步分 MAKE-SET/LINK/FIND-SET 三种情况】 对操作次数进行归纳。
基础情况:MAKE-SET() 创建节点 ,,。,满足 。
归纳步骤:考虑三种操作。
MAKE-SET:创建新节点 ,,。对 满足条件。其他节点的 parent 和 rank 不变,由归纳假设条件成立。
LINK(, ):LINK 使 成为 的父节点()。
- 若 :,严格不等成立。
- 若 : 增加 1,所以 ,严格不等成立。
对于 的原有子节点 (),LINK 后 不变, 不变。但 可能增加 1,所以 仍然成立。
FIND-SET:FIND-SET 只改变 parent 指针(路径压缩),不改变任何 rank。路径压缩使某些节点的 parent 变为根,而根的 rank 是最大的,所以 仍然成立。
秩的单调性: 只在 LINK 中可能增加(当 是根且被选为父节点时)。一旦 不再是根(), 永远不再改变(因为 LINK 只操作根节点,FIND-SET 不改变 rank)。
的单调递增性:如果 是根,其 rank 只增不减。如果 不是根,其 rank 不变,但 的 parent 的 rank 单调递增。综合来看, 随时间单调递增。
推论19.5
从任意节点向上到根的简单路径上,节点秩严格递增。
证明
【由引理19.4:路径上每对相邻节点 ,传递即得秩严格递增】 由引理19.4,路径上每个非根节点 满足 。沿路径逐节点应用此不等式即得。
引理19.6(秩上界 )
每个节点的秩最多为 。
证明
【对 归纳:rank 的根至少 棵初始树合并,故 】 关键观察是 rank 为 的根至少是 棵初始树的根的后代。
对 进行归纳。基础情况 :rank 为 0 的根本身就是一棵树,。
归纳步骤:假设 rank 为 的根至少是 棵不同树的根的后代。考虑 rank 为 的根 。 的 rank 从 增加到 是在某次 LINK 操作中,此时 与另一个 rank 也为 的根 合并。由归纳假设, 之前至少是 棵树的根的后代, 也至少是 棵树的根的后代。合并后,(rank 增加到 )至少是 棵树的根的后代。
由于总共有 个节点(即 棵初始树),rank 为 的根至少需要 个节点。因此 ,即 ,即 。
引理19.7(rank 为 的节点个数上界)
在 个节点的森林中,至多有 个 rank 为 的节点。
证明
【rank 的根子树至少 个节点,不同子树不相交,故 】 由引理19.6 的归纳证明可知,rank 为 的根至少是 棵初始树合并而来的,即以 rank 为 的根为根的子树中至少有 个节点。由于不同根的子树不相交,所有 rank 为 的根的子树中的节点总数至少为 ,其中 是 rank 为 的根的个数。这个总数不超过 ,因此 。
对于非根节点,其 rank 在成为非根后不再改变,而成为非根前的 rank 与某个根的历史 rank 值相同。因此 rank 为 的非根节点的个数也不超过 。综合来看,rank 为 的节点总数至多为 。
势能函数设计
为了使用势能方法证明 的上界,教材设计了一个精巧的势能函数。假设操作序列已被转换为 MAKE-SET、LINK、FIND-SET 序列(每个 UNION 被替换为两次 FIND-SET 加一次 LINK)。
辅助函数 level()(仅对非根节点且 定义):
即 level() 是满足 作用于 后不超过 的最大层级 。
level() 的性质:
-
下界:(由引理19.4,,而 rank 为整数),因此 level() 。
-
上界:(因为 ),因此 level() 。
综上,。
- 单调性:对于给定的非根节点 ,level() 随时间单调递增。这是因为 不变( 不是根),而 单调递增(引理19.4)。
辅助函数 iter()(仅对非根节点且 定义):
即 iter() 是在层级 level() 上,将 迭代作用于 后仍不超过 的最大迭代次数。
iter() 的性质:
-
下界:(由 level 的定义),因此 iter() 。
-
上界:由 level 的定义,。而 ,所以 ,即 iter() 。
综上,。
- 单调性:只要 level() 不变,iter() 随时间单调递增或不变(因为 单调递增)。
节点势能:
引理19.8(势能有界性)
对每个节点 和所有操作计数 ,有 。
证明
【分情况:根/rank=0 直接得界;非根 rank≥1 时 level≤α(n)-1 且 iter≤rank 保证非负】
若 是根或 :,且显然 。
若 不是根且 :
- :因为 (level() ),所以 。又 ,所以 。
- :因为 且 ,所以 。
摊还代价分析
引理19.10(势能变化)
设 是非根节点,第 次操作是 LINK 或 FIND-SET。则 。进一步,若 且 level() 或 iter() 因第 次操作而变化,则 。
证明
【LINK:非根节点势能不增(p.rank 单调递增使 level/iter 递增);FIND-SET:路径压缩后 p 变为根,势能不增】 分情况讨论。
LINK 操作:LINK 只改变根节点的 parent 指针。对于非根节点 ,LINK 不改变 (除非 是被链接的根,但此时 仍然是根,只是 rank 可能增加)。 不变。
若 是 LINK 的父节点(被选为父的根),则 增加 1。由于 单调递增,level() 和 iter() 均单调递增或不变。若 level() 或 iter() 增加,则势能减少至少 1。
若 不是 LINK 涉及的节点,则 不变,level() 和 iter() 均不变,势能不变。
FIND-SET 操作:路径压缩将路径上节点的 parent 指向根。对于路径上的非根节点 (非根节点), 变为根。根的 rank 是最大的,因此 增加(或不变)。level() 和 iter() 均单调递增或不变。若发生变化,势能减少至少 1。
引理19.11(MAKE-SET 摊还代价)
每次 MAKE-SET 的摊还代价为 。
证明
【MAKE-SET 创建 rank=0 节点,势能=0,实际代价 ,摊还 】 MAKE-SET 创建 rank 为 0 的节点 ,势能 。其他节点的势能不变。实际代价 ,势能变化为 0。摊还代价 = 实际代价 + 势能变化 = 。
引理19.12(LINK 摊还代价)
每次 LINK 的摊还代价为 。
证明
【LINK 实际 , 从根变非根势能不增, rank 增加最多贡献 势能增量】 LINK 的实际代价为 。设 LINK 使 成为 的父节点()。
节点 从根变为非根。 的势能从 变为 。因此 的势能不增加。
节点 的 rank 可能增加 1(当 时)。 是根,其势能为 ,增加最多 。
的子节点()的势能: 可能增加 1,由引理19.10,这些节点的势能不增加。
其他节点的势能不变。
总势能增量最多为 (来自 的 rank 增加)。摊还代价 = 。
引理19.13(FIND-SET 摊还代价)
每次 FIND-SET 的摊还代价为 。
证明
【 个节点中至少 个势能减少≥1,摊还代价 】 设查找路径上有 个节点(不含根)。实际代价为 。
关键结论:至少 个节点的势能减少至少 1。
论证如下。查找路径上的节点按从叶到根的顺序排列为 ,其中 是根。路径压缩后, 全部直接指向 。
将路径上的非根节点按 rank 值分组。由推论19.5,路径上节点秩严格递增,因此每个 rank 值最多出现一次。考虑路径上 rank 的节点(至多 个),按 level 值分组。
对于每个 level 值 (),路径上 level 为 的节点中,除最后一个外,每个节点 后面都跟着某个非根节点 ( 在路径上且 在 和 之间),使得路径压缩后 且 。由于 (推论19.5),且路径压缩后 和 有相同的父节点 ,level() 不变(因为 增加到 ),但 iter() 至少增加 1(因为 )。由引理19.10, 至少减少 1。
每个 level 值至多有 1 个”最后一个”节点不满足上述条件。level 值共有 个可能的取值( 到 ),因此至多有 个”最后一个”节点。加上 rank 为 0 的节点(至多 1 个)和路径上紧跟根的节点(至多 1 个),总共有至多 个节点不保证势能减少。
因此,至少 个节点的势能减少至少 1。总势能变化 。
摊还代价 = 。
最终定理
定理19.14
使用按秩合并和路径压缩, 次 MAKE-SET、UNION 和 FIND-SET 操作(其中 次为 MAKE-SET)可以在 时间内完成。
证明
【UNION→2×FIND-SET+LINK,总摊还 】 将每个 UNION 替换为两次 FIND-SET 加一次 LINK,得到等价的 MAKE-SET、FIND-SET、LINK 操作序列。设 MAKE-SET 有 次,FIND-SET 有 次,LINK 有 次,则 (因为每个 UNION 贡献 2 次 FIND-SET 和 1 次 LINK)。
总摊还代价 = (因为 且 )。
由于初始势能 且势能始终非负,总实际代价不超过总摊还代价,即 。
补充理解与拓展
补充:α(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,随时间递增直到 ,此后不再改变。 随时间单调递增。
解答
见上文”核心思想”中引理19.4的完整证明。证明采用对操作次数的归纳法,分 MAKE-SET、LINK、FIND-SET 三种情况讨论。
解题思路提示
对操作次数进行归纳,分 MAKE-SET、LINK、FIND-SET 三种情况讨论。注意 LINK 只操作根节点,FIND-SET 只改变 parent 指针不改变 rank。
题2:19.4-2 证明 rank ≤ ⌊lg n⌋
题目
证明每个节点的秩最多为 。
解答
见上文”核心思想”中引理19.6的完整证明。核心思路是证明”rank 为 的根至少是 棵初始树合并而来的”,通过归纳法完成。
解题思路提示
核心思路是证明”rank 为 r 的根至少是 2^r 棵初始树合并而来的”。这通过归纳法完成,利用了 LINK 只在两个 rank 相等的根合并时才增加 rank 这一事实。
题3:19.4-3 存储 rank 需要的位数
题目
根据练习19.4-2,存储 需要多少位?
解答
由习题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,给出一个简单的证明:仅使用按秩合并(不使用路径压缩)的不相交集合森林上的操作在 时间内运行。
解答
【rank ≤ ⌊lg n⌋ 保证路径长度 ,每次操作 ,总计 】 由习题19.4-2,每个节点的 rank 最多为 。由于路径上秩严格递增(推论19.5),从任何节点到根的路径长度最多为 。
MAKE-SET: 时间。
UNION = FIND-SET + FIND-SET + LINK。两次 FIND-SET 各需 ,LINK 需 。总计 。
FIND-SET:沿 parent 指针走到根,路径长度最多 。
次操作的总时间:每次操作最多 ,总时间为 。
解题思路提示
关键在于:按秩合并保证树高为 O(lg n)(因为 rank 不超过 floor(lg n) 且路径上秩严格递增),因此每次 FIND-SET 为 O(lg n)。没有路径压缩时,这个界不会被打破。
题5:19.4-5 level 是否沿路径单调递增
题目
Dante 教授认为,因为节点秩沿到根的简单路径严格递增,所以节点 level 也必须沿路径单调递增。换言之,如果 且 不是根,则 level() level()。教授正确吗?
解答
【反例:;】 教授不正确。level 不一定沿路径单调递增。
构造反例。考虑以下森林结构(经过特定的 UNION 和路径压缩操作后):
设 ,。计算 level():
- ,所以 level()
- ,所以 level()
- ,所以 level()
- 因此 level()
设 ,。计算 level():
- ,所以 level()
- ,所以 level()
- 因此 level()
此时 level() level(),level 沿路径递减而非递增。
直觉解释: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)可以在 时间内完成。
解答
【 对所有实际 ,替换 为 其余分析平行】 证明 对所有实际 :
- 当 ,即 ,即
- 是一个天文数字,远超任何实际可能的
- 因此 对所有实际 成立
修改势能函数论证:
关键变化是将 替换为 ,并相应调整 level 的上界。
由习题19.4-2,。
level() 的上界调整:
- 因此 level()
势能函数修改为:
其余分析与原始证明完全平行:
- 势能有界性:
- 势能变化:势能不能增加,level 或 iter 变化时势能至少减少 1
- MAKE-SET 为 ,LINK 和 FIND-SET 为
- 最终定理:总时间为
由于 (因为 ),这是一个更紧的上界。
解题思路提示
核心思路是将分析中的 n 替换为 lg(n+1),利用 rank 不超过 floor(lg n) 这一更紧的界。由于 rank 的最大值本身就是 O(lg n),用 lg(n+1) 作为 level 的上界更加”紧凑”,从而得到更小的 alpha’(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章-用于不相交集合的数据结构 #学习/算法导论/不相交集合/按秩合并与路径压缩的分析