leetcode: 連續子數組的最大和

題目來源:力扣

題目描述:

輸入一個整型數組,數組裏有正數也有負數。數組中的一個或連續多個整數組成一個子數組。求所有子數組的和的最大值。
要求時間複雜度爲O(n)。
=============================================
示例1:
輸入: nums = [-2,1,-3,4,-1,2,1,-5,4]
輸出: 6
解釋: 連續子數組 [4,-1,2,1] 的和最大,爲 6。
提示:
1 <= arr.length <= 10^5
-100 <= arr[i] <= 100
===============================================

審題:

該題要求時間複雜度爲O(n),對於連續子數組最大和問題,使用分治算法時間複雜度爲O(NlgN),而使用動態規劃算法時間複雜度爲O(N).因此本題考慮使用動態規劃算法求解.

接下來分析該問題的最優子結構,假設當前數組a[0:n]的連續子數組最大和爲SnS_n, 它與數組a[0:n-1]的連續子數組最大和Sn1S_{n-1}是否存在某種聯繫.進一步分析我們發現,由於數組a[0:n]的連續最大和子樹組可能出現在數組a[0:n]的中間位置,因此其與a[0:n-1]的連續最大和子樹組之間不存在直接的推導關係.由此可見我們從該角度出發無法推導出最優子結構.

上述思路無法進行下去的直接原因在於a[0:n]與a[0:n-1]兩個數組之間相差了一個a[n-1],而由於a[n-1]與a[0:n-1]的連續最大和子數組之間並不存在直接關聯,因此無法推導出最優子結構.

在使用動態規劃求解最優解問題中,通常涉及作出一個初始選擇, 在連續子數組最大和問題中,我們需要確定連續子數組的左右邊界.我們可以作出如下初始選擇:選擇i爲連續最大和子數組的右邊界,假設當前選擇爲最優選擇,我們使用SnS_n表示以數組元素a[n]結尾的連續子樹組最大和.此時我們分析Sn1S_{n-1}SnS_n的關係,我們可以推導出:
if:if: Sn1>=0S_{n-1}>=0, Sn=Sn1+a[n]S_n = S_{n-1} + a[n];
else:else: Sn=a[n]S_{n} = a[n]

我們需要計算所有S[1],S[2],...S[n]S[1],S[2], ...S[n], 選擇最大值作爲連續子樹組的最大和.
注意到在計算SnS_n時,我們需要計算Sn1S_{n-1} ,進一步我們爲了計算Sn1S_{n-1}又涉及計算Sn2S_{n-2}.而在計算Sn1S_{n-1}時,我們又要再次計算Sn2S_{n-2},因此子問題存在重疊.故而應當使用動態規劃算法.
我們可以使用自底向上的動態規劃算法,依次計算S[1],S[2],...S[n]S[1], S[2], ...S[n].

java算法:

class Solution {
    public int maxSubArray(int[] nums) {
        int res = nums[nums.length-1];
        for(int i = nums.length-2; i >=0 ; i--) {
            nums[i] += Math.max(nums[i+1], 0);
            res = Math.max(res, nums[i]);
        }
        return res;
    }
}

時間複雜度分析:

從上述代碼結構不難發現,該算法的時間複雜度爲O(N).我們可是使用動態規劃算法的理論進行驗證.對於任意動態規劃算法,其時間複雜度爲一下兩項乘積:

  1. 算法涉及的子問題個數
  2. 在每一步解決子問題時,需要作出的選擇.

在上述算法中,子問題個數爲N, 我們需要計算S[1], S[2], …S[N].而在解決每一步解決子問題時,我們的選擇爲1, 例如在計算S[3]時,我們的連續最大和子數組右邊界是確定的,我們可以在O(1)的時間判斷其左邊界與S[2]保持一致還是變爲3.因此該算法的時間複雜度爲O(N).

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章