分治递归

一、递归分治

这里的很多资料,都是我从网上看别人的博客,或者从书上摘录下来的,如有侵权,请联系我,立马删帖

1.1 分治法

分治法(Divid-and-Conquer):将问题划分为n个规模较小而结构与原问题相似的子问题;递归地解决这些子问题,然后再合并其结果,就得到原问题地解。

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

  • 分解(Divide):将原问题解成一系列子问题;
  • 解决(Conquer):递归地解决各个子问题。若子问题足够小,则直接求解。
  • 合并(Combine):将子问题的结果合并成原问题的解。

使用条件:

  1. 该问题的规模缩小到一定的程度就可以容易地解决
  2. 该问题可以分解为若干规模较小的相同问题
  3. 利用该问题分解处的子问题的解可以合并为该问题的解
  4. 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子问题。

注:递归算法是一种自底向上逐步求出原来问题的解

1.2代码演示

1.2.1递归

[例题1] 阶层函数递归表示:

int factorial(int n)
{
    if(n==0)
        return 1;
    return n*factorial(n-1);
}

[例题2] Fibonacci数列

int fibonacci(int n)
{
    if(n<=1)
        return 1;
    return fibonacci(n-1)+fibonacci(n-2);
}

[例题3] 排列问题

template<class Type>
void Perm(Type list[],int k,nt m)              // 产生list[k:m]的所有排列
{
    if(k==m)
    {
        for(int i=0;i<=m;i++)
            cout<<list[i];
        cout<<endl;
    }
    else{                                     // 还有很多元素待排列,递归产生排列
        for(int i=k;i<=m;i++)
        {
            Swap(list[k],list[i]);
            Perm(list,k+1,m);
            Swap(list[k],list[i]);
        }   
    }
}
template<class Type>
inline void Swap(Type & a,Type & b)
{
    Type temp=a;
    a=b;
    b=temp;
}

1.2.2 二分搜索技术

算法思想:将n个元素分成个数大致相同的两半,取a[n/2]与x作比较。如果x=[a/2],则找到x,算法终止;如果x<a[n/2],则只在数组a的左半部继续搜索x;如果x>a[n/2],则只在数组a的右半部分继续搜索x。具体算法描述如下:

template<class Type>
int BinarySearch(Type a[],const Type& x,int n)              // 在a[0]<=a[1]<=...<=a[n-1]中搜索x
    // 找到x时返回其在数组中的位置,否则返回-1
{
    int left=0;
    int right=n-1;
    while(left<=right)
    {
        int middle=(left+right)/2;
        if(x==a[middle])
            return middle;
        if(x>a[middle])
            left=middle+1;
        else
            right=middle-1;
    }
    return -1;                                             // 未找到x
}

1.2.3 棋盘覆盖

void ChessBoard(int tr,int tc,int dr,int dc,int size)
{
    if(size==1)
        return;
    int t=tile++;										// L型骨牌号
    s=size/2;										    // 1/4大小,size指2^k(分割棋盘)
    // 覆盖左上角子棋盘
    if(dr<tr+s && dc<tc+s)                                 // 特殊方格在此棋盘中
        ChessBoard(tr,tc,dr,dc,s);						 // 继续递归
    else                                                   // 此棋盘中无特殊方格
    {
        Board[tr+s-1][tc+s-1]=t;                           // 用t号L型骨牌覆盖右下角
        ChessBoard(tr,tc,tr+s-1,tc+s-1,s);                 // 覆盖其余方格
    }
    if(dr<tr+s && dc>=tc+s)                                // 覆盖右上角子棋盘
        ChessBoard(tr,tc+s,dr,dc,s);                       // 特殊方格在此棋盘中
    else                                                   // 此棋盘中无特殊方格
    {
        Board[tr+s-1][tc+s]=t;                             // 用t号L型骨牌覆盖左下角
        ChessBoard(tr,tc+s,tr+s-1,tc+s,s);                 // 覆盖其余方格
    }
    if(dr>=tr+s && dc<tc+s)                                // 覆盖左下角子棋盘
        ChessBoard(tr+s,tc,dr,dc,s);                       // 特殊方格在此棋盘中
    else                                                   // 用t号L型骨牌覆盖右上角
    {
        Board[tr+s][tc+s-1]=t;                            
        ChessBoard(tr+s,tc,tr+s,tc+s-1,s);                 // 覆盖其余方格
    }
    if(dr>=tr+s && dc>=tc+s)                               // 覆盖右下角子棋盘
        ChessBoard(tr+s,tc+s,dr,dc,s);                     // 特殊方格在此棋盘中
    else                                                   // 用t号L型骨牌覆盖左上角
    {
        Board[tr+s][tc+s]=t;
        ChessBoard(tr+s,tc+s,tr+s,tc+s,s);                 // 覆盖其余方格
    }
}

1.2.3 合并排序

基本思想:将待排序元素分成大小一致相同的两个子集合,分别对两个子集合进行排序,最终将排好序的子集合合并成要求的排好序的集合。

合并排序算法可递归地描述如下:

template<class Type>
void MergeSort(Type a[],int left,int right)
{
    if(left<right)                                        // 至少有2个元素
    {
        int i=(left+right)/2;                             // 取中点
        MergeSort(a,left,i);                              
        MergeSort(a,i+1,i);
        Merge(a,b,left,i,right);                          // 合并到数组b
        Copy(a,b,left,right);                             // 复制回数组a
    }
        
}

消去递归后的合并排序算法可描述如下:

/*MergeSort()的递归过程只是将待排序集合一分为二,
直至待排序集合只剩下一个元素为止,然后不断合并两个排好序的数组段。
*/
template<class Type>
void MergeSort(Type a[],int n)
{
    Type *b=new Type [n];
    int s=1;
    while(s<n)
    {
        MergePass(a,b,s,n);                              //合并到数组b
        s += s;
        MergePass(b,a,s,n);                              //合并到数组a
        s+=s;
    }
}

/*
函数MergePass()用于合并排好序的相邻数组段,具体的合并算法由Merge()函数来实现
*/
template<class Type>
void mergepass(Type x[],type y[],int s,int n)            // 合并大小为s的相邻子数组
{
    int i=0;
    while(i<=n-2*s)
    {
        Merge(x,y,i,i+s-1,i+2*s-1);                      // 合并大小为s的相邻2段子数组
        i=i+2*s;
    }
    if(i+s<n)                                            // 剩下的元素个数少于2s
        Merge(x,y,i,i+s-1,n-1);
    else
        for(int j=i;j<=n-1;j++)
            y[j]=x[j];
}
template<class Type>
void Merge(Type c[],Type d[],int l,int m,int r)          // 合并c[1:m]和c[m+1:r]到d[1:r]
{
    int i=1,j=m+1,k=1;
    while((i<=m) && (j<=r))
    {
        if(c[i]<=c[j])
            d[k++]=c[i++];
        else
            d[k++]=c[j++];
        if(i>m)
        {
            for(int q=j;q<=r;q++)
                d[k++]=c[q];
        }
        else
        {
            for(int q=i;q<=m;q++)
                d[k++]=c[q];
        }
            
    }
}

1.2.4 快速排序

template<class Type>
void QuickSort(Type a[],int p,int r)
{
    if(p<r)
    {
        int q=Partition(a,p,r);
        QuickSort(a,p,q-1);                               // 对左半段排序
        QuickSort(a,q+1,r);                               // 对右半段排序
    }
}
/*
对含有n个元素的数组a[0:n-1]进行快速排序只要调用QuickSort(a,0,n-1)即可
上述算法中的函数Partition()以一个确定的基准元素a[p]对子数组a[p:r]进行划分,它是快速排序算法的关键。
*/
template<class Type>
int Partition(Type a[],int p,int r)
{
    int i=p,j=r+1;
    Type x=a[p];
    // 将小于x的元素交换到左边区域,将大于x的元素交换到右边区域
    while(true)
    {
        while(a[++i]<x && i<r);
        while(a[--j]>x);
        if(i>=j)
            break;
        Swap(a[i],a[j]);
    }
    a[p]=a[j];
    a[j]=x;
    return j;
}
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页