1. 程式人生 > 其它 >53.最大子序和-Java實現

53.最大子序和-Java實現

技術標籤:LeetCode資料結構動態規劃leetcode演算法分治演算法

文章目錄

相關標籤

  • 陣列
  • 分治演算法
  • 動態規劃

題目描述

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

示例:

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

進階:

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

解法1:貪心演算法/動態規劃(√)


動態轉移方程:

f[i] = max{ nums[i] , f[i-1] + nums[i] }

初始條件:

f[0] = nums[0]


程式碼:

class Solution {
    public int maxSubArray(int[] nums) {
        int pre = 0;
        int sum = nums[0];
        for (int i = 0; i < nums.length; i++) {
            // 如果前i項的和小於第i項的值 則將pre置為當前項的值
            pre = Math.max(pre + nums[
i] , nums[i]); sum = Math.max(sum , pre); } return sum; } }

時間複雜度:O(n),空間複雜度:O(1)

執行用時:1ms

解法2:分治演算法(√)

class Solution {
    public int maxSubArray(int[] nums) {
        return maxSubArraySum(nums, 0, nums.length-1);
    }

    /**
     * 計算[left,right]的最大子序和
     * @param nums
     * @param left
     * @param right
     * @return
     */
public static int maxSubArraySum(int[] nums, int left, int right) { if (left == right) { return nums[left]; } int mid = (left + right) / 2; // 如果最大子序和落在mid左邊,計算[left,mid]的最大子序和 int maxLeftSum = maxSubArraySum(nums, left, mid); // 如果最大子序和落在mid右邊,計算[mid+1,right]的最大子序和 int maxRightSum = maxSubArraySum(nums, mid + 1, right); // 如果最大子序和跨中點mid int maxMidSum = findMaxCrossingSubArray(nums, left, mid, right); // 返回左、中、右三個值中最大值即為最大子序和 return Math.max(Math.max(maxLeftSum,maxRightSum),maxMidSum); } /** * 計算跨中點mid的最大子序和,分別計算從mid到左和從mid+1到右的最大子序和相加 * @param nums * @param left * @param mid * @param right * @return */ public static int findMaxCrossingSubArray(int[] nums, int left, int mid, int right) { //類似尋找最大最小值的題目,初始值一定要定義成理論上的最小最大值(可能為負陣列) // maxLeftBorderSum :從 mid 往左找到的最大子序和 // maxRightBorderSum :從 mid+1 往右找到的最大子序和 int maxLeftBorderSum = Integer.MIN_VALUE, maxRightBorderSum = Integer.MIN_VALUE, sum = 0; for (int i = mid; i >= left; --i) { sum +=nums[i]; maxLeftBorderSum = Math.max(maxLeftBorderSum,sum); } // 計算從mid+1 到right的最大子序和,sum清零 sum = 0; for (int i = mid + 1; i <= right; ++i) { sum+=nums[i]; maxRightBorderSum = Math.max(maxRightBorderSum,sum); } return maxLeftBorderSum + maxRightBorderSum; } }

時間複雜度:O(n),空間複雜度:O(logn)

執行用時:3ms

「解法2」相較於「解法1」來說,時間複雜度相同,但是因為使用了遞迴,執行的時間略長,空間複雜度也不如解法1優秀,而且難以理解。那麼這種方法存在的意義是什麼呢?
對於這道題而言,確實是如此的。但是仔細觀察「解法2」,它不僅可以解決區間 [0, n - 1],還可以用於解決任意的子區間 [l, r] 的問題。如果我們把 [0, n - 1]分治下去出現的所有子區間的資訊都用堆式儲存的方式記憶化下來,即建成一顆真正的樹之後,我們就可以在 O(logn) 的時間內求到任意區間內的答案,我們甚至可以修改序列中的值,做一些簡單的維護,之後仍然可以在 O(logn) 的時間內求到任意區間內的答案,對於大規模查詢的情況下,這種方法的優勢便體現了出來。這棵樹就是——線段樹。