1. 程式人生 > 實用技巧 >劍指Offer_#42_連續子陣列的最大和

劍指Offer_#42_連續子陣列的最大和

劍指Offer_#42_連續子陣列的最大和

劍指offer

Contents

題目

輸入一個整型陣列,數組裡有正數也有負數。陣列中的一個或連續多個整陣列成一個子陣列。求所有子陣列的和的最大值。
要求時間複雜度為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;
    }
}