五種求解最大連續子數組的算法

      求解最大連續子數組的內容在《算法導論》這本書上面是做爲分治算法的一個例子來進行講解的,書本上面內容(包括習題)提到了三種解決這一問題的算法,下面是我本身使用C++實現這三種方法的代碼和思路放:算法

1、暴力解法數組

      對數組內每個數A[i]進行遍歷,而後遍歷以它們爲起點的子數組,比較各個子數組的大小,找到最大連續子數組函數

#include "stdafx.h"
//暴力法求最大子數組和問題
int _tmain(int argc, _TCHAR* argv[])
{
    int A[8] = { -6, 10, -5, -3, -7, -1, -1 };
    int array_length = sizeof(A) / sizeof(A[0]);//數組大小
    int sum = -10000;//記錄子數組的和
    int low;//記錄子數組的底
    int height;//記錄子數組的高
    for (int i = 0; i < array_length; i++)
    {
        for (int j = i ; j < array_length; j++)
        {
            int subarraysum=0;//所遍歷出來的子數組的和
            //計算遍歷的子數組之和
            for (int k = i; k <= j; k++)
            {
                subarraysum += A[k];
            }
            //找出最大的子數組
            if (subarraysum>sum)
            {
                sum = subarraysum;
                low = i;
                height = j;
            }
        }
    }
    printf("%d  %d  %d", low, height,sum);//將結果打印出來
    getchar();
    return 0;
}

能夠看到這段程序裏面一共嵌套着三層循環,除了最外面的循環會循環n次外內部的循環都比n次小,此程序的時間複雜度爲O(n3this

2、分治法spa

     這一算法在《算法導論》當中有很詳細的說明,並且上面還有僞代碼,我就不囉嗦了,直接放出個人實現代碼。code

 1 #include "stdafx.h"
 2 //分治法求最大子數組和問題
 3 struct PositioASum {
 4     int low;
 5     int high;
 6     int sum;
 7 };
 8 //尋找包含中點位置的最大子數組函數
 9 PositioASum MaxCrossingSubarray(int a[], int low, int mid, int high)
10 {
11     //求中點左邊的最大值和最大位置
12     int maxLeft;//記錄左邊的最大位置
13     int maxSumLeft=-10000;//記錄左邊的最大和
14     int sumLeft=0;
15     for (int i = mid; i >= low; i--)
16     {
17         sumLeft += a[i];
18         if (sumLeft > maxSumLeft)
19         {
20             maxSumLeft = sumLeft;
21             maxLeft = i;
22         }
23     }
24     //求中點右邊的最大值和最大位置
25     int maxRight=mid+1;//記錄右邊的最大位置
26     int maxSumRight = -10000;//記錄右邊的最大和
27     int sumRight = 0;//記錄右邊子數列的和
28     for (int i = mid+1; i <= high; i++)
29     {
30         sumRight += a[i];
31         if (sumRight > maxSumRight)
32         {
33             maxSumRight = sumRight;
34             maxRight = i;
35         }
36     }
37     PositioASum ps;
38     ps.low = maxLeft;
39     ps.high = maxRight;
40     ps.sum = maxSumLeft + maxSumRight;
41     return ps;
42 }
43 //分治法
44 PositioASum FindMaxSubArray(int a[], int low, int high)
45 {
46     if (low == high)
47     {
48         PositioASum ps;
49         ps.low = low;
50         ps.high = high;
51         ps.sum = a[low];
52         return ps;
53     }
54     else{
55         int mid = (low + high) / 2;
56         PositioASum left = FindMaxSubArray(a, low, mid);
57         PositioASum right = FindMaxSubArray(a, mid + 1, high);
58         PositioASum cross = MaxCrossingSubarray(a, low, mid, high);
59         if (left.sum >= cross.sum && left.sum >= right.sum)
60         {
61             return left;
62         }
63         else if (right.sum >= left.sum && right.sum >= cross.sum)
64         {
65             return right;
66         }else{
67             return cross;
68         }
69     }
70 }
71 
72 int _tmain(int argc, _TCHAR* argv[])
73 {
74     int A[8] = {-1,0,0,0,-1};
75     PositioASum result = FindMaxSubArray(A, 0, 4);
76     printf("%d  %d  %d", result.low, result.high, result.sum);//將結果打印出來
77     getchar();
78     return 0;
79 }

算法的時間複雜度爲O(nlogn),因爲本程序是在數組的原地址上面進行的,因此整體的控件複雜度爲遞歸的時間複雜度+數組所佔的空間爲S(n)+S(logn)=S(n)blog

3、根據書本後面習題所提供的方法遞歸

     書本上面提出這樣的一種方法:從數組的左邊界開始,從左到右處理,記錄到目前爲止已經處理過的最大子數組。若已知A[1,2,....,j]的最大子數組,則A[1,2,.....,j,j+1]的最大子數組要麼是A[1,2,....,j]的最大子數組,要麼是某個子數組A[i,....,j+1](1<=i<=j+1)。基於這一思路個人實現過程以下:get

 1 #include "stdafx.h"
 2 //基於算法導論上面的習題4.1-5來實現的算法
 3 int _tmain(int argc, _TCHAR* argv[])
 4 {
 5     int A[8] = { -6, 10, -5, 3, 7, -1, -1 };
 6     int array_length = sizeof(A) / sizeof(A[0]);//數組大小
 7     int maxSum = A[0];//記錄最大子數組的和
 8     int low=0;//記錄最大子數組的底
 9     int high=0;//記錄最大子數組的高
10     for (int i = 0; i < array_length-1; i++)
11     {
12         int sum = 0;
13         //尋找以A[i+1]爲終點的最大子數組
14         for (int j = i+1; j>=0; j--)
15         {
16             sum += A[j];
17             if (sum>maxSum)
18             {
19                 maxSum = sum;
20                 low = j;
21                 high = i + 1;
22             }
23         }
24     }
25     printf("%d  %d  %d", low, high,maxSum);//將結果打印出來
26     getchar();
27     return 0;
28 }

 

4、在線算法it

//在線法求最大子數組和問題
int _tmain(int argc, _TCHAR* argv[])
{
    int A[8] = { -6, 10, -5, 6, -7, -1, -1 };
    int array_length = sizeof(A) / sizeof(A[0]);//數組大小
    int sum = 0;//記錄子數組的和
    int thisSum = 0;
    int low=0;//記錄子數組的底
    int height=0;//記錄子數組的高
    for (int i = 0; i < array_length; i++)
    {
        thisSum += A[i];
        if (thisSum > sum)
        {
            sum = thisSum;
        }
        else if (thisSum < 0)
        {
            thisSum = 0;
        }
    }
    printf("%d",sum);//將結果打印出來
    getchar();
    return 0;
}

 

理解這個算法的關鍵是:使用thisSum來計算當前連續子數組的和,若是thisSum小於0,那麼不管後面再加上什麼數字都只會讓子數組變小,因此拋棄當前子數組,從新開始計算子數組的值。

能夠看到這個算法的時間複雜度爲O(n)並且控件複雜度爲S(n),是解決這一個問題很是有效的一個算法。

5、另一種稍好的算法

 1 int _tmain(int argc, _TCHAR* argv[])
 2 {
 3     int n;//存儲所輸入的數組長度
 4     scanf("%d", &n);
 5     int* b = (int*)malloc(n*sizeof(int));//存儲所出入的數組
 6     for (int i = 0; i < n; i++)
 7     {
 8         scanf("%d", &b[i]);
 9     }
10     int sum=0;//最大子數列和
11     //尋找最大子數列
12     for (int i = 0; i < n; i++)
13     {
14         int thisSum = 0;//當前數列
15         for (int j = i; j < n; j++)
16         {
17             thisSum += b[j];
18             if (thisSum>sum)
19             {
20                 sum = thisSum;
21             }
22         }
23     }
24     if (sum < 0)
25     {
26         printf("%d",0);
27     }else{
28         printf("%d", sum);
29     }
30     system("pause");
31     return 0;
32 }

     相比於暴力解法每一次都從i到j地從新計算一次,這種算法每一次只須要在原來計算的基礎上面加上一個數,因此這種算法少了一層循環,時間複雜度爲O(n2)是一種比暴力解法要高效的解法

相關文章
相關標籤/搜索