分治法(求最大子序列和)


 1 //求出最大子序列 4 ,-3,5,-2,-1,2,6,-2 
 2 #include <stdio.h>
 3 int max (int a,int b,int c)
 4 {
 5     int ret;
 6     if(a > b)
 7     {
 8         ret = a;
 9     }else
10     if(a <= b)
11     {
12         ret = b;
13     }
14     if(ret >= c)
15     return ret;
16     else
17     return c;
18 }
19  int Findmaxsum(int box[],int size,int left,int right)      //參數(數組名,數組大小,左邊界,右邊界)
20 {
21     int mid = (right + left) / 2;
22     if(left == right)                                        //分治遞歸要注意出口條件
23     {
24         return box[left];
25     }
26     int leftsum = Findmaxsum(box,size,left,mid );           //求出左半區最大子序列和 ,要有遞歸信任,不要糾結層層深入,假設該函數是正確的。 
27     int rightsum = Findmaxsum(box,size,mid + 1,right);       //求出右半區最大子序列和
28     int leftbordersum = 0;
29     int rightbordersum = 0;
30     int i;
31     int thissum = 0;
32     for(i = mid + 1 ;i <= right;i++)                         //求出含有中間分界點的右半區最大子序列和  (如果最大子序列橫跨中間分界點,那么肯定包含中間分界點,)
33     {
34         thissum += box[i];
35         if(rightbordersum < thissum)
36         {
37             rightbordersum = thissum;
38         }
39      } 
40      thissum = 0;
41      for(i = mid ;i >= left;i--)                          //求出含有中間分界點的左半區最大子序列和
42      {
43          thissum += box[i];
44          if(leftbordersum < thissum)
45          {
46              leftbordersum = thissum;
47          }
48      }
49      int midsum = leftbordersum + rightbordersum;               //橫跨左右半區最大子序列和
50      return max(midsum,leftsum,rightsum);                        //左半區最大子序列和,右半區最大子序列和,跨半區最大子序列和,三者中最大的為所求者
51
52     
53 }
54 int main ()
55 {
56     int box[8] = {4,-3,5,-2,-1,2,5,-2};
57     int ret = 0;
58     ret = Findmaxsum(box,8,0,7);
59     printf("%d",ret);
60     return 0 ;
61  } 

此算法時間復雜度為 O(NlogN).

思考1:思考如何求得。

可以先寫出遞推關系式,設T(n)為規模為n時程序運行的時間。

1.觀察到26,27行運用到了遞歸將問題規模縮小了一半且運用了兩次,因此T(n) = 2T(n/2);

2.第35至50得兩個循環規模為n/2即O(n);因此T(n)=2T(n/2)+O(n),由於我們所求者也為大O所以可以寫成T(n)=2T(n/2)+n;

 

接下來可以有多種解決方法,此處介紹最笨但容易理解簡單的迭代方法

1.設迭代了k次

    T(n)=2T(n/2)+n

            =2*(2T(n/4)+n/2)+n=2^2T(n/2^2)+2n

            ...

    =2^k T(n/2^k) + kn

由於最后要迭代到基准情形即: n/2^k = 1;因此我們可得k = logn;

2.將k帶入:

    =2^(logn)T(n/2^logn)+nlogn;

    =nT(1) + nlogn

顯然可以求得:O(nlogn);

 

思考2:突然想到:為什么二分查找的時間復雜度O(logn)?同樣本質是二分為什么此處O(nlogn)多出n?

 

  二者方法都為“分”,但是二分查找“分”后並不需要"合",而此處的分治法需要“分”求得結果后再“合”。

可以這么想,二分查找O(logn)不難理解,但其本質在於只要找到所求元素,對於其他元素並不關心,

每次遞歸中不符合條件的元素,直接不需要程序繼續處理。程序筆直的朝着縮小范圍的目的地前進,最后

找到一個目標。

  而分治“分”后其實並沒有將其中的某一半舍棄,他其實需要處理到每一個元素。簡而言之,二分法“分”

后直接將一半舍棄,分治需要將每一半都處理。分到最后每單個元素都處理過了,然后容易想到既然一個元素

是O(logn),呢么所有n個元素在其前加個n得到O(nlogn)也就自然而然了。

 

思考3:究竟為什么問題“分”后治之會將時間復雜度降低?

  以求最小子序列和為例,用最普通的暴力搜索,第一個與后面n-1個元素做比較,第二個元素n-2次....共[(1+n-1)*(n-1)]/2次;

他的時間復雜度為O(N^2).可將規模為n時計算量近似看成n^2次,用分治法將問題規模縮小一半,則n/2時,計算量n^2/4。由於

有兩半增加了問題數量所以乘以2,所以n^2/4 * 2 = n^2/2;比原來計算量n^2減少n^2/2次。由此減少了計算量,降低了時間復雜度。

關鍵點在於N^2是個平方階,將n規模縮小計算量會以平方階下降,雖然乘以n(線性階)但與下降相比還是少。

 

本人還才疏學淺,如有錯誤的地方非常感謝大家的指正。!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM