求最大連續子數列和(只掃描一次數列)

一、什麼是求最大連續子數列和

首先來看看這是個怎樣的問題的,問題描述:一個整型數組,數組裏有正數也有負數。數組中連續的一個或多個整數組成一個子數組,每個子數組都有一個和,求所有子數組的和的最大值。注意:當全是負數的情況時,返回最大的那個負數


二、解題思路

這個問題的思路其實非常簡單,從左到右掃描數組,在掃描過程中,記錄數組的負數的個數和掃描過中數據中的最大值,並累加每個掃描到的數據的和,假設用變量thisSum(初值爲0)保存,如果當前的累加值大於之前的累加值的最大值 (例如用變量sum記錄,初值爲0),則把當前的最大值保存爲最大值(sum = thisSum),如果thisSum小於0,則把thisSum設置爲0並重新進行累加。一直這樣掃描數組,直到把數組掃描完。


由於thisSum已經小於0,也就是說之前統計的和可以捨棄,因爲把當前的元素累加之後,結果反而小了。例如把數組分成三部分AiB,因爲A的值大於0,A+i的值小於0,所以如果從B開始從新累加,則其值一定比包括i然後去累加B的結果大,因爲i小於0,而B中的和卻不一定比在A之前累加的和大。


由於如果數組全是負數時,要返回最大的負數,而從上面所說的說法中,我們可以看到當前累加總和(thisSum)總是與0進行比較,如果小於0則把thisSum置爲0,所以當數組全是負數時,thisSum和數組的最大子序列之和(sum)總是爲0,而與現實有點不一樣,所以就要記錄負數的數量,當負數的數量等於元素的個數(即全是負數)時,就要把最大連續子序列和置爲最大的負數。這也是前面所說的,在掃描過程中記錄負數的個數和最大元素的作用。


三、實現代碼

int MaxSum(int* a,int n)
{
    int sum = 0; //用於記錄最大的連續子數組和
    int flag = 0;//用於記錄負數的個數
    int MaxNum = *a;//用於記錄數組中最大的數
    int ThisSum = 0;//用於記錄當前的連續子數組和
    for(int i = 0; i < n; ++i)
    {
        if(a[i] < 0) //如果無素爲負數,則把flag的值加1
            ++flag;
        if(MaxNum < a[i]) //記錄數組當前的最大值
            MaxNum = a[i];
        ThisSum += a[i]; //累加更新當前的子數組之和
        if(ThisSum > sum)
        {
            //若當前連續子數組之和大於記錄的子數組之和
            //則設置最大連續子數組之和爲當前的和
            sum = ThisSum;
        }
        else if(ThisSum < 0)
        {
            //如果當前連續子數組之和小於0,則拋棄之前的連續子數組,
            //從此元素的下一個元素重新計算連續子數組之和
            ThisSum = 0;
        }
    }
    //若全是負數,最大值爲數組中的最大無素
    if(flag == n)
        sum = MaxNum;
    return sum;
}
我們再來看看測試結果吧,測試代碼如下:

int main()
{
    int a[100] = {1, -2, 3, 10, -4, 7, 2, -5};
    cout<<MaxSum(a,8)<<endl;
    return 0;
}
運行結果如下:


從運行結果和測試數據來看,最大的連續子數組應該是3,10,-4,7,2.它們的和就爲18.


四、時間複雜度和空間複雜度分析

從代碼和上面的解說可以看到,這個算法的時間複雜度只爲O(N),而且常數爲1,即只需要掃描一次數組即可完成任務。而且用到的輔助空間也非常少,只有四個變量,空間複雜度爲O(1)。


五、完整代碼代碼下載地址:

https://github.com/ljianhui/Arithmetic

文件名:max_sum_of_continuous_sub_array.cpp

發佈了126 篇原創文章 · 獲贊 456 · 訪問量 268萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章