1. 程式人生 > >Leetcode題解之動態規劃(3)最大子序和

Leetcode題解之動態規劃(3)最大子序和

題目:https://leetcode-cn.com/explore/interview/card/top-interview-questions-easy/23/dynamic-programming/56/

題目描述:

  最大子序和

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

示例:

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

進階:

如果你已經實現複雜度為 O(n

) 的解法,嘗試使用更為精妙的分治法求解。

 

思路: 第一種 暴力破解。O(N^2)沒啥好說的

第二種 掃描法O(N) :
當我們加上一個正數時,和會增加;當我們加上一個負數時,和會減少。如果當前得到的和是個負數,那麼這個和在接下來的累加中應該拋棄並重新清零,不然的話這個負數將會減少接下來的和。

第三種:動態規劃:

設sum[i]為以第i個元素結尾且和最大的連續子陣列。假設對於元素i,所有以它前面的元素結尾的子陣列的長度都已經求得,那麼以第i個元素結尾且和最大的連續子陣列實際上,要麼是以第i-1個元素結尾且和最大的連續子陣列加上這個元素,要麼是隻包含第i個元素,即sum[i] 
= max(sum[i-1] + a[i], a[i])。可以通過判斷sum[i-1] + a[i]是否大於a[i]來做選擇,而這實際上等價於判斷sum[i-1]是否大於0。由於每次運算只需要前一次的結果,因此並不需要像普通的動態規劃那樣保留之前所有的計算結果,只需要保留上一次的即可,因此演算法的時間和空間複雜度都很小

//暴力列舉:
  private int max = Integer.MIN_VALUE;
    public int maxSubArray(int[] nums) {
        int sum;
        for (int i = 0; i < nums.length; i++) {// 子序列左端點
            sum = 0;
            for (int j = i; j < nums.length; j++) {// 子序列右端點
                sum += nums[j];// 這裡就相當於每次根據前一次的序列來計算新的序列
                if (sum > max)
                    max = sum;
            }
        }
        return max;
    }

//掃描法:
class Solution {
    public int maxSubArray(int[] nums) {
        int current=nums[0];
        int sum=nums[0];
        //我們考慮如果全是負數,那麼返回最大的負數,如果最後的和為正,那麼就使用掃描法
        for(int i=1;i<nums.length;i++) {
            if(current<0)current=nums[i];//當前數小於0 肯定會捨去(否則將會影響接下來的和),換為下一個數
            else current+=nums[i];//如果當前數不小於0,那麼他會對接下來的和有積極影響
            if(current>sum)sum=current;//這裡既實現了負數返回最大也實現了掃描法
            //這裡其實已經隱式的列舉了所有可能,保留了所有可能的最大值
        }
        return sum;
    }
}


//動態規劃
class Solution {
    public int maxSubArray(int[] nums) {// 動態規劃法
        int sum=nums[0];
        int n=nums[0];
        for(int i=1;i<nums.length;i++) {
            if(n>0)n+=nums[i];
            else n=nums[i];
            if(sum<n)sum=n;
        }
        return sum;
    }
}