1. 程式人生 > >LeetCode 題解之 53. Maximum Subarray(連續子陣列的最大和問題)

LeetCode 題解之 53. Maximum Subarray(連續子陣列的最大和問題)

53. Maximum Subarray(連續子陣列的最大和問題)

題目描述和難度

  • 題目描述:

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

示例:

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

進階:

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

思路分析

求解關鍵:連續子陣列的問題,一般我們著眼於以當前遍歷到的元素結尾的那個子陣列,這樣做分析會簡化問題。

參考解答

參考解答1

public class Solution {

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

    /**
     * 定義狀態:
     * dp[i] : 表示以 nums[i] 結尾的連續子陣列的最大和
     * <p>
     * 狀態轉移方程:
     * dp[i] = max{num[i],dp[i-1] + num[i]}
     *
     * @param nums
     * @return
     */
    public int maxSubArray(int
[] nums) { int len = nums.length; if (len == 0) { return 0; } int[] dp = new int[len]; dp[0] = nums[0]; for (int i = 1; i < len; i++) { dp[i] = Math.max(nums[i], dp[i - 1] + nums[i]); } // 最後這一步,是求一個全域性的最優值 int
res = dp[0]; for (int i = 1; i < len; i++) { res = Math.max(res,dp[i]); } return res; } }

參考解答2 :和參考解答 1 是一樣的,只不過在遍歷的過程中,就把最優解解求出來了。

public class Solution2 {

    /**
     * 和 Solution 一樣,空間複雜度更小
     * 時間複雜度:O(n)
     * 空間複雜度:O(1)
     *
     * @param nums
     * @return
     */
    public int maxSubArray(int[] nums) {
        int len = nums.length;
        if (len == 0) {
            return 0;
        }
        int segmentSum = nums[0];
        int res = nums[0];
        for (int i = 1; i < len; i++) {
            segmentSum = Math.max(nums[i], segmentSum + nums[i]);
            res = Math.max(res, segmentSum);
        }
        return res;
    }

    public static void main(String[] args) {
        int[] nums = {-2, 1, -3, 4, -1, 2, 1, -5, 4};
        Solution2 solution = new Solution2();
        int maxSubArray = solution.maxSubArray(nums);
        System.out.println(maxSubArray);
    }
}

參考解答3:使用分治思想,個人覺得稍顯繁瑣,但是我們可以通過這個問題了解分治思想。

public class Solution3 {

    /**
     * 使用分治演算法完成計算
     * https://www.geeksforgeeks.org/divide-and-conquer-maximum-sum-subarray/
     *
     * @param nums
     * @return
     */
    public int maxSubArray(int[] nums) {
        int len = nums.length;
        if (len == 0) {
            return 0;
        }
        return maxSubArraySum(nums, 0, len - 1);
    }

    /**
     * 一定會包含 nums[mid] 這個元素
     *
     * @param nums
     * @param l
     * @param m
     * @param r
     * @return
     */
    private int maxCrossingSum(int[] nums, int l, int m, int r) {
        int sum = 0;
        int leftSum = Integer.MIN_VALUE;
        // 左半邊包含 nums[mid] 元素,最多可以到什麼地方
        // 走到最邊界,看看最值是什麼
        // 計算以 mid 結尾的最大的子陣列的和
        for (int i = m; i >= l; i--) {
            sum += nums[i];
            if (sum > leftSum) {
                leftSum = sum;
            }
        }
        sum = 0;
        int rightSum = Integer.MIN_VALUE;
        // 右半邊不包含 nums[mid] 元素,最多可以到什麼地方
        // 計算以 mid+1 開始的最大的子陣列的和
        for (int i = m + 1; i <= r; i++) {
            sum += nums[i];
            if (sum > rightSum) {
                rightSum = sum;
            }
        }
        return leftSum + rightSum;

    }

    /**
     * @param nums
     * @param l
     * @param r
     * @return
     */
    private int maxSubArraySum(int[] nums, int l, int r) {
        if (l == r) {
            return nums[l];
        }
        int mid = l + (r - l) / 2;
        return max3(maxSubArraySum(nums, l, mid),
                maxSubArraySum(nums, mid + 1, r),
                maxCrossingSum(nums, l, mid, r));
    }

    private int max3(int num1, int num2, int num3) {
        return Math.max(num1, Math.max(num2, num3));
    }
}