劍指Offer_#42_連續子陣列的最大和
阿新 • • 發佈:2020-07-15
劍指Offer_#42_連續子陣列的最大和
劍指offerContents
題目
輸入一個整型陣列,數組裡有正數也有負數。陣列中的一個或連續多個整陣列成一個子陣列。求所有子陣列的和的最大值。
要求時間複雜度為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
思路分析
方法1:暴力搜尋
兩層迴圈,遍歷所有可能的連續子陣列,比較得出他們當中最大的連續子陣列和。時間超限。面試最好別這麼寫。
方法2:分析規律得到啟發式的演算法
可以從頭到尾逐漸計算連續和,分析求最大連續和的過程,如下圖。
發現我們可以用兩個變數來儲存“累加的陣列和”及“最大的陣列和”兩個數字,在遍歷陣列的過程中不斷更新這兩個變數。
迴圈結束即可得到最大連續子陣列和。
方法3:動態規劃
- 狀態定義:
dp[i]
是以nums[i]
作為結尾的連續子陣列的最大和。 - 狀態轉移:
dp[i] = dp[i - 1] + nums[i] (dp[i - 1] > 0)
dp[i] = nums[i] (dp[i - 1] <= 0)
其實這個思路跟方法2寫出的程式碼一摸一樣,只不過感覺這樣分析更符合“套路”,更通用一些。
解答
解法1:暴力搜尋
class Solution {
public int maxSubArray(int[] nums) {
int max = nums[0];
int sum = nums[0];
for(int i = 0;i <= nums.length - 1;i++){
//進入第二層迴圈之前,sum需要重置,因為這時開始算另一個連續子陣列了
sum = 0;
for(int j = i; j <= nums.length - 1;j++){
//計算從nums[i]開始的所有連續和,作為最大值的候選
sum += nums[j];
max = Math.max(max, sum);//更新max
}
}
return max;
}
}
超出時間限制。
解法2:觀察規律
class Solution {
public int maxSubArray(int[] nums) {
int tmpSum = nums[0];//儲存當前的連續和(可能是最大連續和)
int maxSum = nums[0];//儲存結果
for(int num:nums){
//如果加上之前的連續還沒有當前的num大,就可以拋棄前面的tmpSum了
tmpSum = Math.max(tmpSum + num,num);
maxSum = Math.max(tmpSum, maxSum);
}
return maxSum;
}
}
解法3:動態規劃
class Solution {
public int maxSubArray(int[] nums) {
int res = nums[0];//儲存結果
int cur = nums[0];//儲存dp[i]
int pre = 0;//儲存dp[i-1]
for(int num:nums){
//當dp[i-1] > 0:dp[i] = dp[i-1] + num
//當dp[i-1] <= 0:dp[i] = num
cur = num + Math.max(pre, 0);
res = Math.max(res ,cur);
//狀態更新,本輪迴圈的cur,是下輪迴圈的cur
pre = cur;
}
return res;
}
}