分治法就是把一个大的问题分成若干个小的子问题,然后递归地解决小问题,最后再合并子问题的解。回顾一下归并排序,就是一个典型的分治法的例子:第一步把整个数组一分为二,第二步递归地对每个子数组排序,第三步合并两个有序数组。归并排序的复杂度T(n)=2T(n/2)+Θ(n),可以用主定理的2解出,为Θ(nlogn)。
二分查找
二分查找是分治法一个简单的例子。从一个有序的数组中找出x,这个问题可以用这样的分治法:1. 把x与数组的中间元素比较,2. 如果x小于中间的数,那么x一定在数组左边,果x大于中间的数,那么x一定在数组右边,因此只在一个子数组中递归查找,3. 合并这一步没有任何计算量。复杂度T(n)=T(n/2)+Θ(1),解出结果为Θ(logn)。
代码:
int bi_search(int a[],int p,int r,int x)
{
int q=p+(r-p)/2;
if(a[q]==x)
return q;
else if(a[q]>x)
return bi_search(a, p, q,x);
else
return bi_search(a, q+1, r, x);
}
乘方问题
给定一个数x,和一个正整数n,求xn。
朴素的解法就是把n个x相乘,即x·x·x…·x,乘n-1次,复杂度是Θ(n)。如果用分治法呢,可以在非线性时间内解决这个问题吗?
我们可以把xn划分为两个xn/2,即
x n = { x n / 2 ⋅ x n / 2 n 为 偶 数 x n − 1 / 2 ⋅ x n − 1 / 2 ⋅ x n 为 奇 数 x^n=\begin{cases} x^{n/2}·x^{n/2} & n为偶数 \\ x^{n-1/2}·x^{n-1/2}·x & n为奇数 \\ \end{cases} xn={
xn/2⋅xn/2xn−1/2⋅xn−1/2⋅xn为偶数n为奇数
T(n)=T(n/2)+Θ(1)=Θ(logn)
斐波那契数列
斐波那契数列的定义是:
F ( n ) = { 0 n = 0 1 n = 1 F ( n − 1 ) + F ( n − 2 ) n > 1 F(n)=\begin{cases} 0 & n=0 \\ 1 & n=1 \\ F(n-1)+F(n-2) & n>1 \\ \end{cases} F(n)=⎩⎪⎨⎪⎧01