1. 程式人生 > >陣列連續子序列的最大的和;四種演算法,四種時間複雜度

陣列連續子序列的最大的和;四種演算法,四種時間複雜度

給定一組資料:31, -41, 59, 26, -53, 58, 97, -93, -23, 84。要求找到這組資料中連和最大的子序列,相信很多人都看過這個演算法,能夠直接給出線性時間複雜度的解法。但是還是有其他解法的,而且是循序漸進的,從O(n^3), O(n^2), O(n*logn)到O(n)。

在開始演算法之前要進行一下約定,如果當前輸入的都是負數的時候,綜合最大的子序列是空的,總和為0.

第一種方法就是brute-force解法,就是對滿足所有的0<=i < j < n的組合(i,j)進行迭代,對於每個整數對都要計算a[i,j]的總和,這個演算法有個特點,也就是造成時間複雜度很高的缺點,對於每次計算的結果沒有記錄,只是簡單的將此次計算的結果和上次計算的結果進行比較,求得當前最大值後,就給扔掉了。下面給出演算法:

  1. current_max = 0;  
  2. sum = 0;  
  3. for(i = 0; i < n; i++) {  
  4.     for(j = i; j < n; j++) {  
  5.         sum = 0;  
  6.         for(k = i; k <= j; k++)  
  7.             sum += a[k];  
  8.         if(sum > current_max)  
  9.             current_max = sum;  
  10.     }  
  11. }  


很清楚的可以看出,對i和j的操作過程中,只是對當前和與上次的和做簡單的比較,之後就扔棄了。想一想,可不可以這樣做,對於特定的i,遍歷所有可行的j值,記錄此次(i,j)的操作,對於(i,j+1)的操作可以在(i,j)所得和的基礎上簡單的加a[j+1]就可以了。

下面給出演算法:

  1. current_max = 0;  
  2. for(i = 0; i < n; i++) {  
  3.     sum = 0;  
  4.     for(j = i; j < n; j++) {  
  5.         sum += a[j];  
  6.         if(sum > current_max)  
  7.             current_max = sum;  
  8.     }  
  9. }  
這個時間複雜度比上次的有所降低。

同時,這個問題還可以用分治演算法來解決,可能會有疑問,因為分治演算法是用來解決子問題之間沒有聯絡的問題的。而求連續子序列和最大值得問題更多的和動態規劃相關,因為用動態規劃很容易解決這個問題。但是分治也可以,這種演算法很巧妙,巧妙的讓人不可思議。給出演算法程式碼:

  1. #include<stdio.h>
  2. int max(int a, int b, int c) {  
  3.     if(a > b) {  
  4.         if(a > c) {  
  5.             return a;  
  6.         } else {  
  7.             return c;  
  8.         }  
  9.     } else {  
  10.         if(b > c) {  
  11.             return b;  
  12.         } else {  
  13.             return c;  
  14.         }  
  15.     }  
  16. }  
  17. int get_max(int *a, int low, int high) {  
  18.     int m = (low + high) / 2;  
  19.     int i;  
  20.     int sum, lmax, rmax;  
  21.     if(low > high)  
  22.         return 0;  
  23.     if(low == high)  
  24.         return a[low] > 0 ? a[low] : 0;  
  25.     sum = lmax = 0;  
  26.     for(i = m; i >= low; i--){  
  27.         sum += a[i];  
  28.         if(sum > lmax) {  
  29.             lmax = sum;  
  30.         }  
  31.     }  
  32.     sum = rmax = 0;  
  33.     for(i = m + 1; i <= high; i++) {  
  34.         sum += a[i];  
  35.         if(sum > rmax) {  
  36.             rmax = sum;  
  37.         }  
  38.     }  
  39.     return max(lmax + rmax, get_max(a, low, m), get_max(a, m + 1, high));  
  40. }  
  41. void main() {  
  42.     int a[] = {31, -41, 59, 26, -53, 58, 97, -93, -23, 84};  
  43.     int n = sizeof(a) / sizeof(int);  
  44.     int result = get_max(a, 0, n - 1);  
  45.     printf("max_sum = %d\n", result);  
  46. }  
這個演算法的巧妙之處就在於計算lmax和rmax之處,計算lmax是從m開始到low結束,計算rmax是從m+1開始,在high結束。

最後一種方法就是使用動態規劃了,相信很多人能給出此種演算法。直接給出程式碼:

  1. #include<stdio.h>
  2. int get_max2(int *a, int n) {  
  3.     int current_max = a[0];  
  4.     int final_max = a[0];  
  5.     int i;  
  6.     for(i = 1; i < n; i++) {  
  7.         if(current_max + a[i] > 0) {  
  8.             current_max += a[i];  
  9.         } else {  
  10.             current_max = 0;  
  11.         }  
  12.         if(current_max > final_max) {  
  13.             final_max = current_max;  
  14.         }  
  15.     }  
  16.     return final_max;  
  17. }  
  18. void main() {  
  19.     int a[] = {31, -41, 59, 26, -53, 58, 97, -93, -23, 84};  
  20.     int n = sizeof(a) / sizeof(int);  
  21.     int rst = get_max2(a, n);  
  22.     printf("max_sum = %d\n", rst);  
  23. }