53 最大子序和

2021-10-05 17:30:44 字數 2847 閱讀 8039

給定乙個整數陣列 nums ,找到乙個具有最大和的連續子陣列(子陣列最少包含乙個元素),返回其最大和。

示例:輸入: [-2,1,-3,4,-1,2,1,-5,4],

輸出: 6

解釋: 連續子陣列 [4,-1,2,1] 的和最大,為 6。

高階:如果你已經實現複雜度為 o(n) 的解法,嘗試使用更為精妙的分治法求解。

方法一:動態規劃

思路和演算法

假設 nums 陣列的長度是 n,下標從 0 到 n - 1。我們用 ai代表 nums[i],用 f(i)代表以第 i 個數結尾的「連續子陣列的最大和」,那麼很顯然我們要求的答案就是:

max(0<=i<=n-1)

因此我們只需要求出每個位置的 f(i),然後返回 f 陣列中的最大值即可。那麼我們如何求 f(i)呢?我們可以考慮ai單獨成為一段還是加入 f(i - 1)對應的那一段,這取決於 ai和 f(i - 1) + ai的大小,我們希望獲得乙個比較大的,於是可以寫出這樣的動態規劃轉移方程:

f(i) = max

不難給出乙個時間複雜度 o(n)、空間複雜度 o(n) 的實現,即用乙個 f 陣列來儲存 f(i)的值,用乙個迴圈求出所有 f(i)。考慮到 f(i)只和 f(i - 1)相關,於是我們可以只用乙個變數 pre 來維護對於當前 f(i)的 f(i - 1)的值是多少,從而讓空間複雜度降低到 o(1),這有點類似「滾動陣列」的思想。

class solution 

return max;

}}

方法二:分治

思路和演算法

這個分治方法類似於「線段樹求解 lcis 問題」的 pushup 操作。 也許讀者還沒有接觸過線段樹,沒有關係,方法二的內容假設你沒有任何線段樹的基礎。當然,如果讀者有興趣的話,推薦看一看線段樹區間合併法解決 多次詢問 的「區間最長連續上公升序列問題」和「區間最大子段和問題」,還是非常有趣的。

我們定義乙個操作 get(a, l, r) 表示查詢 aa 序列 [l, r][l,r] 區間內的最大子段和,那麼最終我們要求的答案就是 get(nums, 0, nums.size() - 1)。如何分治實現這個操作呢?對於乙個區間 [l, r][l,r],我們取 m = \lfloor \frac \rfloorm=⌊ 

2l+r

​    

⌋,對區間 [l, m][l,m] 和 [m + 1, r][m+1,r] 分治求解。當遞迴逐層深入直到區間長度縮小為 11 的時候,遞迴「開始回公升」。這個時候我們考慮如何通過 [l, m][l,m] 區間的資訊和 [m + 1, r][m+1,r] 區間的資訊合併成區間 [l, r][l,r] 的資訊。最關鍵的兩個問題是:

我們要維護區間的哪些資訊呢?

我們如何合併這些資訊呢?

對於乙個區間 [l, r][l,r],我們可以維護四個量:

lsum 表示 [l, r][l,r] 內以 ll 為左端點的最大子段和

rsum 表示 [l, r][l,r] 內以 rr 為右端點的最大子段和

msum 表示 [l, r][l,r] 內的最大子段和

isum 表示 [l, r][l,r] 的區間和

以下簡稱 [l, m][l,m] 為 [l, r][l,r] 的「左子區間」,[m + 1, r][m+1,r] 為 [l, r][l,r] 的「右子區間」。我們考慮如何維護這些量呢(如何通過左右子區間的資訊合併得到 [l, r][l,r] 的資訊)?對於長度為 11 的區間 [i, i][i,i],四個量的值都和 a_ia 

i​    

相等。對於長度大於 11 的區間:

首先最好維護的是 isum,區間 [l, r][l,r] 的 isum 就等於「左子區間」的 isum 加上「右子區間」的 isum。

對於 [l, r][l,r] 的 lsum,存在兩種可能,它要麼等於「左子區間」的 lsum,要麼等於「左子區間」的 isum 加上「右子區間」的 lsum,二者取大。

對於 [l, r][l,r] 的 rsum,同理,它要麼等於「右子區間」的 rsum,要麼等於「右子區間」的 isum 加上「左子區間」的 rsum,二者取大。

當計算好上面的三個量之後,就很好計算 [l, r][l,r] 的 msum 了。我們可以考慮 [l, r][l,r] 的 msum 對應的區間是否跨越 mm——它可能不跨越 mm,也就是說 [l, r][l,r] 的 msum 可能是「左子區間」的 msum 和 「右子區間」的 msum 中的乙個;它也可能跨越 mm,可能是「左子區間」的 rsum 和 「右子區間」的 lsum 求和。三者取大。

class solution ;

status pushup(status l, status r) ;

};status get(vector&a, int l, int r) ;

int m = (l + r) >> 1;

status lsub = get(a, l, m);

status rsub = get(a, m + 1, r);

return pushup(lsub, rsub);

}int maxsubarray(vector& nums)

};

複雜度分析

假設序列 aa 的長度為 nn。

時間複雜度:假設我們把遞迴的過程看作是一顆二叉樹的先序遍歷,那麼這顆二叉樹的深度的漸進上界為 o(\log n)o(logn),這裡的總時間相當於遍歷這顆二叉樹的所有節點,故總時間的漸進上界是 o(\sum_^ 2^) = o(n)o(∑ 

i=1logn

​    

2 i−1

)=o(n),故漸進時間複雜度為 o(n)o(n)。

空間複雜度:遞迴會使用 o(\log n)o(logn) 的棧空間,故漸進空間複雜度為 o(\log n)o(logn)。

題外話

53 最大子序和

給定乙個整數陣列nums,找到乙個具有最大和的連續子陣列 子陣列最少包含乙個元素 返回其最大和。示例 輸入 2,1,3,4,1,2,1,5,4 輸出 6 解釋 連續子陣列 4,1,2,1 的和最大,為 6。兩個變數,乙個是ans,用來儲存 更新子串行 乙個maxn,始終儲存著當前最大的子串行。子串行...

53 最大子序和

題目 給定乙個整數陣列nums,找到乙個具有最大和的連續子陣列 子陣列最少包含乙個元素 返回其最大和。示例 輸入 2,1,3,4,1,2,1,5,4 輸出 6解釋 連續子陣列 4,1,2,1 的和最大,為 6。思路 因為要考慮連續,所以需注意兩點,與當前比較的必定包含當前節點的上乙個節點,前n個節點...

53 最大子序和

給定乙個整數陣列nums,找到乙個具有最大和的連續子陣列 子陣列最少包含乙個元素 返回其最大和。示例 輸入 2,1,3,4,1,2,1,5,4 輸出 6解釋 連續子陣列 4,1,2,1 的和最大,為 6。高階 如果你已經實現複雜度為 o n 的解法,嘗試使用更為精妙的分治法求解。package le...