算法系列2、分治算法

来源:互联网 发布:fstab自动挂载yum 编辑:程序博客网 时间:2024/05/02 05:56

    • 1基本概念
    • 2分治法适用的情况
    • 3分治法的基本步骤
    • 4设计程序的思维过程
    • 5分治法的复杂性分析
    • 6示例代码-归并排序

1、基本概念

字面上的解释是“分而治之”,就是把一个复杂的问题

1) 把它分成两个或多个更小的问题;

2) 分别解决每个小问题;

3) 把各小问题的解答组合起来,即可得到原问题的解答。

这个技巧是很多高效算法的基础,如排序算法(快速排序,归并排序),傅立叶变换(快速傅立叶变换)……

2、分治法适用的情况

分治法所能解决的问题一般具有以下几个特征

  • 1)原问题可以分解为多个子问题

    • 这些子问题与原问题相比,只是问题的规模有所降低,其结构和求解方法与原问题相同或相似。
  • 2) 原问题在分解过程中,递归地求解子问题。

    • 由于递归都必须有一个终止条件,因此,当分解后的子问题规模足够小时,应能够直接求解。
  • 3) 利用该问题分解出的子问题的解可以合并为该问题的解;

    • 应能够采用某种方式、方法合并或构造出原问题的解。

不难发现,在分治策略中,由于子问题与原问题在结构和解法上的相似性,用分治方法解决的问题,大都采用了递归的形式。在各种排序方法中,如归并排序、堆排序、快速排序等,都存在有分治的思想

3、分治法的基本步骤

  • step1 分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题;

  • step2 求解:若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题

  • step3 合并:将各个子问题的解合并为原问题的解。

它的一般的算法设计模式如下

Divide-and-Conquer(P)

1. if |P|≤n02. then return(ADHOC(P))3. 将P分解为较小的子问题 P1 ,P2 ,...,Pk4. for i←1 to k5. do yi ← Divide-and-Conquer(Pi) △ 递归解决Pi6. 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的解。

4、设计程序的思维过程

实际上就是类似于数学归纳法,找到解决本问题的求解方程公式,然后根据方程公式设计递归程序。

  • 1、一定是先找到最小问题规模时的求解方法

  • 2、然后考虑随着问题规模增大时的求解方法

  • 3、找到求解的递归函数式后(各种规模或因子),设计递归程序即可。

5、分治法的复杂性分析

一个分治法将规模为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)

6、示例代码-归并排序

#include <stdlib.h>#include <stdio.h>void Merge(int sourceArr[],int tempArr[], int startIndex, int midIndex, int endIndex){    int i = startIndex, j=midIndex+1, k = startIndex;    while(i!=midIndex+1 && j!=endIndex+1)    {        if(sourceArr[i] >= sourceArr[j])            tempArr[k++] = sourceArr[j++];        else            tempArr[k++] = sourceArr[i++];    }    while(i != midIndex+1)        tempArr[k++] = sourceArr[i++];    while(j != endIndex+1)        tempArr[k++] = sourceArr[j++];    for(i=startIndex; i<=endIndex; i++)        sourceArr[i] = tempArr[i];}//内部使用递归void MergeSort(int sourceArr[], int tempArr[], int startIndex, int endIndex){    int midIndex;    if(startIndex < endIndex)    {        midIndex = (startIndex + endIndex) / 2;        MergeSort(sourceArr, tempArr, startIndex, midIndex);        MergeSort(sourceArr, tempArr, midIndex+1, endIndex);        Merge(sourceArr, tempArr, startIndex, midIndex, endIndex);    }}int main(int argc, char * argv[]){    int a[8] = {50, 10, 20, 30, 70, 40, 80, 60};    int i, b[8];    MergeSort(a, b, 0, 7);    for(i=0; i<8; i++)        printf("%d ", a[i]);    printf("\n");    return 0;}
0 0