本文来自我的个人博客 https://www.zhangshenghai.com/posts/57540/
分治法是将一个复杂的问题分成一些规模较小而结构与原问题相似的子问题,递归地解这些子问题,然后将各子问题的解合并得到原问题的解。
分治法在每一层的递归上都有三个步骤:
- 分解(Divided):将原问题分解成一系列子问题。
- 解决(Conquer):递归地解各子问题。
- 合并(Combine):将子问题的结果合并成原问题的解。
假设我们将原问题分解成a个子问题,每一个的大小是原问题的1/b。如果分解和合并的时间各为D(n)和C(n),则可得到递归式:
合并排序(Merge Sort)
当合并排序的运行时间如下分解:
- 分解:仅计算出子数组的中间位置,需要常量时间,故。
- 解决:递归地解两个规模为n/2的子问题,时间为。
- 合并:在一个含有n个元素的子数组上,MERGE过程的运行时间为,则。
因此,合并排序的最坏运行时间的递归表示是:
由之前第一章介绍的主定理,我们可以得到,这里的代表。同样,我们也可以通过递归树得到相同的答案。
最大子数组问题(Maximum Subarray Problem)
对于一个具有连续连续值的数组A,寻找A的和最大的非空连续子数组,我们称这样的连续子数组为最大子数组。例如,对于下图的数组,A[1...16]的最大子数组是A[8...11],其和为43。在实际的例子中可表示在第8天买入股票,并在第11天卖出,获得的收益为43美元。
使用分治法可以求解最大子数组问题。首先找到数组的中间点,将数组分为左右两个数组,那么最大子数组可能存在于下列三种情况之一:
- 完全位于左边的数组中。
- 完全位于右边的数组中。
- 跨越了左右两个数组。
对于前两种情况,使用同样的方式递归地划分为规模最小的子数组求解即可。对于第三种情况,我们采用的算法是从中间点向两边遍历,分别求出两边的最大子数组,然后将左右两边的子数组相加即为跨越中点的最大子数组。
下面给出算法的伪代码:
对FIND-MAXIMUM-SUBARRAY算法的运行时间进行分析:
首先计算出子数组的中间位置,需要常量时间,然后递归地解两个规模为n/2的子问题,时间为,调用FIND-MAX-CROSSING-SUBARRAY花费了时间,则,用主方法或递归树求解此递归式可得。
斐波那契数列(Fibonacci Number)
对于斐波那契数,我们有。利用分治策略,我们可以将斐波那契数列转换为矩阵乘幂的问题,如下图所示:
使用分治法,我们将矩阵递归地分解成两个相同的矩阵,再将这两个矩阵相乘即可。
故。
整数乘法(Integer Multiplication)
假设x, y分别为两个n-bit的整数,如果要将它们相乘,模拟使用手动乘法得到的时间复杂度是,考虑分治法。
令,如。那么,这里的时间复杂度,使用主方法可得。
Anatolii, Karatsuba在1962年提出了一个只需要三次子乘法就可以完成运算的算法,其时间复杂度的递归表示为,使用主方法可得。
矩阵乘法(Matrix multiplication)
给定一个n维矩阵X和Y,计算Z=XY。我们可以使用分治法求解这个问题。
- 分解:将X和Y分解为n/2维的矩阵。
- 解决:使用8次矩阵乘法递归地将这些n/2维的矩阵相乘。
- 合并:使用4次矩阵加法将矩阵合并。
下面给出一个例子:
1969年Strassen提出了一个只需要7次矩阵乘法就可以完成运算的算法,算法将原矩阵分为7个新的子矩阵如下图所示:
然后进行计算:
此算法的时间复杂度为。
凸包问题(The Convex Hull Problem)
假设平面上有一系列点,过某些点作一个多边形,使这个多边形能把所有点都“包”起来,当这个多边形是凸多边形的时候,我们就叫它凸包,凸包问题就是求构成凸包的点,如下图所示。
使用蛮力法是最容易想到的,思路是由两点确定一条直线,如果剩余的点都在这条直线的同一侧,那么认为这两个点是构成凸包的点。蛮力法的时间复杂度为。
下面我们介绍解决凸包问题的分治法,下面为具体步骤和图例:
- 将所有点放在二维坐标系里,那么横坐标最大的两个点一定是凸包上的点(具体可以用反证法证明,这里不展开说)。直线将点集合分为了两部分,分别叫上包和下包。
- 对于上包,求距离直线最远的点,即下图中的。
- 作直线和,把左侧的点当作上包,把右侧的点也当作是上包。
- 重复步骤2、3。
- 对下包也做类似的操作。
分治法的时间复杂度为。
三格骨牌问题(Tromino Tiling)
对于三格骨牌问题,同样可以用分治法求解,解决这个问题的思想是每次都将平板分成四块同等大小的子平板。
例如,在插入三格骨牌时,将平板分成四块,由于洞位于左上方的子平板,因此将三个骨牌放置为如图所示的位置,以确保四块子平板的大小相等。递归地进行这个过程即可得出结果。
最邻近点问题(Finding the Closest Pair of Points)
顾名思义,最邻近点问题即在平面点集中找出距离最近的两个点。使用时间复杂度为的蛮力法可以解决这个问题。
下面我们介绍解决这个问题的分治法,首先将点集划分为两个部分,然后递归地寻找最近的点,若找到最近的两个点之间的距离为,则寻找是否存在分别属于两个部分的点之间的距离小于,算法的时间复杂度为。