1. 程式人生 > >演算法原理與分析之分治法

演算法原理與分析之分治法

一.目錄

1.演算法基本原理

2.經典問題

二.分治法基本原理

    分而治之,先將原問題的規模下降,分解為子問題,此所謂“分”,然後解決子問題,此為“治”。

    分治法的基本思想是將一個規模為n的原問題分解為k個規模較小的子問題,這些子問題互相獨立且與原問題相同。遞迴地解這些子問題,然後將子問題的解合併為原問題的解。

     分治法的虛擬碼:

   v divide_and_conquer(proplem p){//n為問題規模

       if(|p|<n0)//n0為一閾值

       solve(p);

     else{

      divide p into smaller subproblem P1,P2,...Pk;

      for(i=1;i<=k;i++){

       yi=divide_and_conquer(Pi);

      return merge(y1,y2,...,yk);

}

}

}

   可以看到,分治法與遞迴是一對孿生兄弟,有分治法的地方就有遞迴的身影。

    使用分治法時,要將原問題進行規模分解,分解為獨立的子問題。一般來說,將原問題分解為兩個大小相同的子問題可以得到將好的效率。 

    對分治法的複雜度分析是通過對遞迴的複雜度進行分析。遞迴複雜度分析方法有三種,即主定理,遞迴樹,代入法。

三.經典問題

  3.1.合併排序

  3.2.快速排序

  3.3.線性時間選擇

 3.1合併排序 

     合併排序是利用分治法對n個元素進行排序的方法。其基本思想,先將原元素集合分解為規模大小基本相同的兩個子集合,然後依次遞迴地對子問題進行排序。最後將排好序的子集合合併為所要求的排好序的集合。

    虛擬碼:

    void merge_sort(Type a[],int left,int right){

      if(left<right){    //至少兩個元素

         int i = (left+right)/2;

        merge_sort(a,left,i);

        merge_sort(a,i+1,right);

        merge(a,b,left,i,right);//合併到陣列b

        copy(a,b,left,right);//複製回陣列a

}

}

     void merge(Type origin[],Type destint[],int left,int middle,int right){

          int i=left; int k=left; int j=middle+1;

         while(i<middle &&  j<right){

               if(origin[i]<origin[j]) destin[k++] = origin[i++];

              else  destin[k++] = origin[j++];

  }

      //處理剩餘元素

         if(i<middle){

          while(i<middle) destin[k++] = origin[i++];

         else

         while(j<right) destin[k++] = origin[j++];

}

  }

   分治法的複雜度分析:

       分治法的遞迴語句複雜度為T(n/2);

       合併(merge) n個元素的複雜度為O(n);

      複製(copy) n個元素的複雜度為O(n);

                               O(1) n<=1;

               T(n) ={

                              T(n/2) + O(n); n>=2;

   可以得出分治法的平均複雜度為O(nlogn);