第8.8.1章: 主定理(附录)

English

概述

许多算法是递归定义的。为了分析它们的时间复杂度,我们常常写出一个递推关系,描述运行时间如何取决于规模更小的子问题。

主定理是求解下面这类常见递推式的工具:


T(n) = a · T(n / b) + f(n)
      

其中:

本附录将教你如何用主定理计算并证明时间复杂度,并给出归并排序和快速排序等例子。

1. 主定理

考虑递推式:


T(n) = a · T(n / b) + f(n)
      

定义临界指数:


n^(log_b(a))
      

把 f(n) 与 n^(log_b(a)) 作比较。

情形 1:f(n) 更小


f(n) = O(n^(log_b(a) - ε))
      

对某个 ε > 0。

则:


T(n) = Θ(n^(log_b(a)))
      

情形 2:f(n) 与临界指数相当


f(n) = Θ(n^(log_b(a)) · ln^k(n))
      

对某个 k ≥ 0。

则:


T(n) = Θ(n^(log_b(a)) · ln^(k+1)(n))
      

情形 3:f(n) 更大


f(n) = Ω(n^(log_b(a) + ε))
      

对某个 ε > 0,并且满足正则性条件。

则:


T(n) = Θ(f(n))
      

2. 例子:归并排序

归并排序把数组分成两半,递归排序每一半,然后用线性时间把它们合并起来。


T(n) = 2 · T(n / 2) + Θ(n)
      

这里:

临界指数:


n^(log_2(2)) = n
      

由于 f(n) = Θ(n),属于情形 2。


T(n) = Θ(n · ln(n))
      

3. 例子:快速排序(平均情况)

在平均情况下,快速排序把数组分成两个规模大致相等的子问题,划分耗时线性时间。


T(n) = 2 · T(n / 2) + Θ(n)
      

这与归并排序的递推式相同,因此:


T(n) = Θ(n · ln(n))
      

这就解释了为什么快速排序平均情况下很快。

4. 例子:快速排序(最坏情况)

最坏情况下(枢轴总是最小或最大元素),递推式变成:


T(n) = T(n - 1) + Θ(n)
      

把递推式展开:


T(n) = n + (n - 1) + (n - 2) + ... + 1 = Θ(n^2)
      

它不满足主定理的适用条件,但可以直接计算。

5. 例子:二叉树递归

考虑一个递归处理二叉树的函数:


T(n) = 2 · T(n / 2) + Θ(1)
      

这里:

临界指数:


n^(log_2(2)) = n
      

由于 f(n) = Θ(1) 比 n 小,属于情形 1。


T(n) = Θ(n)
      

这符合直觉:每个结点访问一次,总时间是线性的。

6. 主定理不适用的情况

主定理不适用于:

例如斐波那契递推式:


T(n) = T(n - 1) + T(n - 2) + Θ(1)
      

它以 Θ(2^n) 增长,但这无法用主定理推出。

要点

主定理是分析分治算法的重要工具之一。有了它,你可以快速而严谨地算出一大批递归算法的时间复杂度。