当一个算法包含对其自身的递归调用时,我们往往可以用递归方程或递归式来描述其运行时间,该方程根据在较小输入上的运行时间来描述在规模为n的问题上的总运行时间。然后,我们可以使用数学工具来求解该递归式并给出算法性能的界。
分析算法运行时间的递归式来自基本模式的三个步骤。如前所述,我们假设T(n)是规模为n的一个问题的运行时间。若问题规模足够小,如对某个常量c,n<=c,则直接求解需要常量时间,我们将其写作O(1).假设把原问题分解成a个子问题,每个子问题是原问题的1/b.(对归并排序,a和b都为2,然而,我们将看到在许多分治算法中,a!=b)为求解一个为n/b的子问题,需要T(n/b)的时间,所以需要aT(n/b)的时间来求解a个子问题。如果分解问题成子问题需要时间D(n),合并子问题的解成原问题的解需要时间C(n),那么得到递归式
O(1) 若n<=c
T(n) =
aT(n/b)+D(n)+C(n) 其他
归并排序算法的分析
虽然MERGE-SORT的伪代码在元素的数量不是偶数时也能正确地工作,但是假定原问题规模是2的幂,那么基于递归式的分析将被简化。这时每个分解步骤将产生规模刚好为n/2的两个子序列。
下面我们分析建立归并排序n个数的最坏情况运行时间T(n)的递归式。归并排序一个元素需要常量时间。当有n>1个元素时,我们分解运行时间如下:
分解:分解步骤仅仅计算子数组的中间位置,需要常量时间,因此,D(n)=O(n).
解决:我们递归地求解两个规模均为n/2的子问题,将贡献2T(n/2)的运行时间
合并:我们已经注意到在一个具有n个元素的子数组上过程MERGE需要O(n)的时间,所以C(n)=O(n).
当为了分析归并排序而把函数D(n)与C(n)相加时,我们是在把一个O(n)函数与另一个O(1)函数相加。相加的和是n的一个线性函数,即O(n).把它与来自"解决"步骤的项2T(n/2)相加,将给出归并排序的最坏情况运行时间T(n)的递归式:
O(1) 若n=1
T(n) = (2.1)
2T(n/2)+O(n) 若n>1
在第4章,我们将看到"主定理",可以用该定理来证明T(n)为O(nlgn),其中lgn代表log2 n.因为对数函数比任何线性函数增长要慢,所以对足够大的输入,在最坏情况下,运行时间为O(nlgn)的归并排序将优于运行时间为O(n^2)的插入排序。
为了直观地理解式(2.1)的解为什么是T(n)=O(nlgn),我们并不需要主定理。把递归式(2.1)重写为:
c 若n=1
T(n) = (2.2)
2T(n/2)+cn 若n>1
其中常量c代表求解规模为1的问题所需的时间以及在分解步骤与合并步骤处理每个数组元素所需的时间