3.2 分治法
分治(Divide-and-Conquer)就是“分而治之”的意思,分治法的基本思想就是将一个规模为n的问题分解为k个规模较小的子问题,这些子问题互相独立且与原问题相关联。通过递归方式来求解这些子问题,然后将各个子问题的解合并成原问题的解,如图3-1所示。它的一般的算法设计模式代码如下所示。
图3-1 分治法思想
在用分治法设计算法时,最好使子问题的规模大致相同,即将一个问题分成大小相等的k个子问题(许多问题取k=2)。这种使子问题规模大致相等的做法出自一种平衡子问题的思想,通常比子问题规模不等的做法要好。
例3.3 分治法求数列中最大最小值。
问题描述:输入一个数组A[1,…, n],求出A中的最大值与最小值。
方法思想:使用分治法的思想,首先把数组分成两部分,再把这两部分中的每一部分再分成两部分,一直递归分解下去直到每一部分小于或等于两个数为止,然后比较这两个数大小,然后回弹比较直到递归的最外层,就可以找到数组中的最大最小值,代码如下所示。
伪代码已经给出了算法的逻辑,只需要根据Python语言的特性转换成Python的实现即可,实现代码如下:
程序运行结果为:
现在来分析上述代码中算法的时间复杂度,用n表示待查找数组中元素的个数,用T(n)表示该问题的时间复杂度。根据算法中的递归关系可以得出:
根据上一章讲述的算法分析方法,可以得到。
总的来说,分治法所能解决的问题一般具有以下几个特征:
1)可行性。该问题的规模缩小到一定的程度时就可以容易地解决;因为问题的计算复杂性一般是随着问题规模的增加而增加,因此大部分问题满足这个特征。
2)可分解性。该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质,这条特征是应用分治法的前提,它也是大多数问题可以满足的,此特征反映了递归思想的应用。
3)可合并性。利用该问题分解出的子问题的解可以合并为该问题的解;能否利用分治法完全取决于问题是否具有这条特征,如果具备了前两条特征,而不具备第三条特征,则可以考虑贪心算法或动态规划。
4)独立性。该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子问题。这条特征涉及分治法的效率,如果各子问题是不独立的,则分治法要做许多不必要的工作,重复地解公共的子问题,此时虽然也可用分治法,但用动态规划或贪心法会更好。
下面重点来分析分治法的复杂性,从一般设计模式看,用分治法设计的程序通常是一个递归算法。若一个分治法将规模为n的问题分成k个规模为n/m的子问题去解。假设规模为1的问题耗费1个单位时间,再设将原问题分解为k个子问题以及将k个子问题的解合并为原问题的解需用f(n)个单位时间。用T(n)表示该分治法求解规模为|P|=n的问题所需的计算时间,则有:
通过迭代法求得方程解为:
其中,nlogmk为基本子问题所消耗的时间,则为合并部分所消耗的时间。设f(n)=Θ(nd), d≥0。依照主定理可得: