力扣 – 劍指 Offer 42. 連續子數組的最大和
題目
思路1(分析數組的規律)
- 我們可以從頭到尾逐個累加,若之前的累加和小於0,那就從丟棄之前的累加,從當前開始重新累加,同時在遍歷過程中比較記錄下最大值
curSum
記為當前最大值,為 0,以[-2,1,-3,4,-1,2,1,-5,4]
為例:- 首先加上 -2,此時
curSum
為 -2 - 由於 -2 小於 0,所以丟棄,然後再加上 1,此時
curSum
為 1 - 再加上 -3,此時
curSum
為 -2 - 由於 -2 小於 0,所以再次丟掉,然後加上 4,此時
curSum
為4 - 然後加上 -1,此時
curSum
為 3 - 再加上 2,此時
curSum
為 5 - 再加上 1,此時
curSum
為 6 - 再加上 -5,此時
curSum
為 1 - 最後再加上最後一個 4,此時
curSum
為 5 - 在這每次遍歷中,我們使用一個變量
res
存儲最大值,可以找到最大值為 6
- 首先加上 -2,此時
代碼
class Solution {
public int maxSubArray(int[] nums) {
int length = nums.length;
// 最大總和值
int res = Integer.MIN_VALUE;
// 當前總和
int curSum = 0;
for (int i = 0; i < length; i++) {
if (curSum < 0) {
// 如果 i 之前總和值小於0,那就從 i 開始重新計算
curSum = nums[i];
} else {
// 否則加上當前的值
curSum += nums[i];
}
// 尋找最大值
if (curSum > res) {
res = curSum;
}
}
return res;
}
}
複雜度分析
- 時間複雜度:\(O(N)\)
- 空間複雜度:\(O(1)\)
思路2(動態規劃)
-
和思路一差不多動態規劃就是利用歷史記錄,避免重複計算。所以也是從頭到尾逐個累加,若之前的累加和小於0,那就從丟棄之前的累加,從當前開始重新累加,我們可以定義一個
dp
數組,dp[i]
代表的意義就是以i
結尾的子數組的最大值。因此我們可以得出狀態轉移方程:\[dp[i] = \begin{cases} dp[i-1]+nums[i], & \text{if } dp[i-1]<0 \\ nums[i], & \text{if } dp[i-1]\geq0 \end{cases}
\] -
既然我們可以得到以
i
結尾子數組的最大值,那麼只需要從這些最大值中找到最大的一個就是結果了~
代碼
class Solution {
public int maxSubArray(int[] nums) {
int length = nums.length;
int[] dp = new int[length];
dp[0] = nums[0];
int res = dp[0];
for (int i = 1; i < length; i++) {
dp[i] = dp[i-1] > 0 ? dp[i-1]+nums[i] : nums[i];
res = Math.max(res, dp[i]);
}
return res;
}
}
可以進一步優化:
class Solution {
public int maxSubArray(int[] nums) {
int length = nums.length;
// 記錄子數組中的最大值
int res = Integer.MIN_VALUE;
// 記錄前一段子數組之和
int preSum = 0;
for (int num : nums) {
// 意思也是判斷前一個字數組之和是否小於0
preSum = Math.max(num, preSum + num);
// 然後記錄最大值
res = Math.max(res, preSum);
}
return res;
}
}
複雜度分析
- 時間複雜度:\(O(N)\)
- 空間複雜度:\(O(1)\)