前言
终于到期末了,平时欠的债现在该还了(哭唧唧),我打算一天一章,快速复习(预习)算法设计与分析,希望我不要挂科啊!!!
一、基本概念
在计算机科学中,分治法是一种很重要的算法。字面上的解释是“分而治之”,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。这个技巧是很多高效算法的基础,如排序算法(快速排序,归并排序),傅立叶变换(快速傅立叶变换)
任何一个可以用计算机求解的问题所需的计算时间都与其规模有关。问题的规模越小,越容易直接求解,解题所需的计算时间也越少。例如,对于n个元素的排序问题,当n=1时,不需任何计算。n=2时,只要作一次比较即可排好序。n=3时只要作3次比较即可,…。而当n较大时,问题就不那么容易处理了。要想直接解决一个规模较大的问题,有时是相当困难的.
二、主要思想
将一个难以直接求解的问题分解为一些规模足够小的相同问题,并且可以利用这些子问题求出原问题的解。
一般来说,利用分治法产生的小问题都是原问题的较小规模问题,此时,可以通过递归来更加方便的求解。
由此观之,递归分治像一对孪生兄弟,经常一起出现,并由此产生了许多高效的算法。
三、适用条件
- 该问题缩小的规模足够小可以很方便的求解
- 该问题可以分解为若干个规模较小的问题,即该问题具有最优子结构性质
- 该问题分解出的子问题可以合并成原问题的解
- 该问题分解出的子问题相互独立,没有重合
基本问题都会满足第一点,因为问题会随规模的增大而增大,这是肯定的了。
第二条是能够使用分治法的前提,只有存在最优子结构性质才能考虑分治
第三条是关键,如果分治分出来的子问题无法组合起来,那这个分解就是没有意义的。当问题满足第二条而不满足第三条时,可以考虑贪心或动态规划。
要是子问题重合太多,会导致许多重复计算,此时最好使用动态规划算法。
四、求解过程
分治法在每一层递归上都有三个步骤:
step1 分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题;
step2 解决:若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题
step3 合并:将各个子问题的解合并为原问题的解。
它的一般的算法设计模式如下:
Divide-and-Conquer(P)
1. if |P|≤n0
2. then return(ADHOC(P))
3. 将P分解为较小的子问题 P1 ,P2 ,...,Pk
4. for i←1 to k
5. do yi ← Divide-and-Conquer(Pi) △ 递归解决Pi
6. T ← MERGE(y1,y2,...,yk) △ 合并子问题
7. return(T)
其中|P|表示问题P的规模;n0为一阈值,表示当问题P的规模不超过n0时,问题已容易直接解出,不必再继续分解。ADHOC(P)是该分治法中的基本子算法,用于直接解小规模的问题P。因此,当P的规模不超过n0时直接用算法ADHOC(P)求解。算法MERGE(y1,y2,...,yk)是该分治法中的合并子算法,用于将P的子问题P1 ,P2 ,...,Pk的相应的解y1,y2,...,yk合并为P的解。
五、时间复杂度分析
一个分治法将规模为n的问题分成k个规模为n/m的子问题去解。设分解阀值n0=1,且adhoc解规模为1的问题耗费1个单位时间。再设将原问题分解为k个子问题以及用merge将k个子问题的解合并为原问题的解需用f(n)个单位时间。用T(n)表示该分治法解规模为|P|=n的问题所需的计算时间,则有:
T(n)= k T(n/m)+f(n)
六、递归分治实践
1.归并排序
采用分治算法,将待排序数组往小里划分,直到只有单个元素,直接返回,然后两两归并。
代码:
#include<stdio.h>
int s[60];
int He(int* s1,int* s2,int a,int b,int c);
int Copy(int* ls,int* f,int a,int b)
{
// printf("ppp\n");
int i=0,j=0;
for(i=a;i<=b;i++)
{
ls[i]=f[j];
j++;
}
}
int Merge(int* List,int left,int right)
{
// printf("%d %d\n",left,right);
if(right<=left)
{
return 0;
}
else
{
int i=(left+right)/2;
Merge(List,left,i);
Merge(List,i+1,right);
He(List,s,left,i,right);
Copy(List,s,left,right);
}
int l=0;
}
int He(int* s1,int* s2,int a,int b,int c)
{
// printf("a,b,c %d %d %d\n",a,b,c);
int i,j,k,m;
j=a;
k=b+1;
for(i=0;i<=c-a;i++)
{
if(s1[j]<s1[k])
{
s2[i]=s1[j];
j++;
}
else
{
s2[i]=s1[k];
k++;
}
// printf("s2[i]=%d i=%d\n",s2[i],i);
if(j>b||k>c)
{
break;
}
}
if(j>b)
{
// printf("j:%d\n",j);
for(m=k;m<=c;m++)
{
s2[++i]=s1[m];
}
}
else
{
//printf("j:%d i:%d\n",j,i);
for(m=j;m<=b;m++)
{
s2[++i]=s1[m];
// printf("%d\n",s1[m]);
}
}
}
int main()
{
int num;
scanf("%d",&num);
int List[num];
int i=0;
for(i=0;i<60;i++)
{
s[i]=-1;
}
for(i=0;i<num;i++)
{
scanf("%d",List+i);
}
Merge(List,0,num-1);
for(i=0;i<num;i++)
{
printf("%d ",List[i]);
}
}
随时复习,随时更新…