递归与分治算法

前言

终于到期末了,平时欠的债现在该还了(哭唧唧),我打算一天一章,快速复习(预习)算法设计与分析,希望我不要挂科啊!!!

一、基本概念

在计算机科学中,分治法是一种很重要的算法。字面上的解释是“分而治之”,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。这个技巧是很多高效算法的基础,如排序算法(快速排序,归并排序),傅立叶变换(快速傅立叶变换)

任何一个可以用计算机求解的问题所需的计算时间都与其规模有关。问题的规模越小,越容易直接求解,解题所需的计算时间也越少。例如,对于n个元素的排序问题,当n=1时,不需任何计算。n=2时,只要作一次比较即可排好序。n=3时只要作3次比较即可,…。而当n较大时,问题就不那么容易处理了。要想直接解决一个规模较大的问题,有时是相当困难的.

二、主要思想

将一个难以直接求解的问题分解为一些规模足够小的相同问题,并且可以利用这些子问题求出原问题的解。
一般来说,利用分治法产生的小问题都是原问题的较小规模问题,此时,可以通过递归来更加方便的求解。
由此观之,递归分治像一对孪生兄弟,经常一起出现,并由此产生了许多高效的算法。

三、适用条件

  1. 该问题缩小的规模足够小可以很方便的求解
  2. 该问题可以分解为若干个规模较小的问题,即该问题具有最优子结构性质
  3. 该问题分解出的子问题可以合并成原问题的解
  4. 该问题分解出的子问题相互独立,没有重合

基本问题都会满足第一点,因为问题会随规模的增大而增大,这是肯定的了。
第二条是能够使用分治法的前提,只有存在最优子结构性质才能考虑分治
第三条是关键,如果分治分出来的子问题无法组合起来,那这个分解就是没有意义的。当问题满足第二条而不满足第三条时,可以考虑贪心或动态规划。
要是子问题重合太多,会导致许多重复计算,此时最好使用动态规划算法。

四、求解过程

分治法在每一层递归上都有三个步骤:

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]);
    }
}

随时复习,随时更新…

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值