Loading

[LeetCode] 53. Maximum Subarray(最大子数组)

Description

Given an integer array nums, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum.

给定一个整数数组 nums,找一个连续子数组(至少包含一个数),这个子数组的元素和最大并返回这个和。

Follow up

If you have figured out the O(n) solution, try coding another solution using the divide and conqur approach, which is more subtle.

如果你已经用 \(O(N)\) 的方法解决了,尝试用更微妙的分治法解决它。

Examples

Example 1

Input: nums = [-2,1,-3,4,-1,2,1,-5,4]
Output: 6
Explanation: [4,-1,2,1] has the largest sum = 6.

Example 2

Input: nums = [1]
Output: 1

Example 3

Input: nums = [0]
Output: 0

Example 4

Input: nums = [-1]
Output: -1

Example 5

Input: nums = [-2147483647]
Output: -2147483647

Constraints

  • 1 <= nums.length <= 2*10^4

  • -2^31 <= nums[i] <= 2^31-1

Solution

动态规划法:

假设有数组 nums,我们需要求以下标 i 结尾的子数组的最大值,那么有以下两种情况:

  1. 包括下标 i 之前的元素,这样只要求以 i - 1 下标为结尾的子数组的最大值,加上 i 即可

  2. 不包括下标 i 之前的元素,即单纯只有 nums[i](因为之前元素的子数组和的最大值可能为负数)

以上两种情况取最大值,得到状态转移方程:

\[result[i] = \max(result[i - 1] + nums[i], nums[i]) \]

由于当前状态只与前一个状态有关,所以本来需要开一个数组的空间,现在可以压缩到一个变量

localMax = max(nums[i], localMax + nums[i])

最后的代码如下:

import kotlin.math.max

class Solution {
    fun maxSubArray(nums: IntArray): Int {
        if (nums.isEmpty()) {
            return 0
        }
        if (nums.size == 1) {
            return nums[0]
        }
        var result = nums[0]
        var currentMax = nums[0]
        for (i in 1..nums.lastIndex) {
            currentMax = max(nums[i], currentMax + nums[i])
            result = max(currentMax, result)
        }
        return result
    }
}

分治法:

对于分治法,需要考虑以下问题:这个最大子数组位于哪里?有三种情况:

  1. 完全位于左半边

  2. 完全位于右半边

  3. 跨越左半边和右半边

于是可以将数组分成两半,对左右两边的求解可以得到情况 1 和情况 2,对于情况 3 也只需要以 \(O(N)\) 时间从中间位置扫描一遍数组即可,代码如下:

import kotlin.math.max

class Solution {
    fun maxSubArray(nums: IntArray): Int {
        return if (nums.isEmpty()) {
            0
        } else {
            maxSubArray(nums, 0, nums.lastIndex)
        }
    }

    private fun maxSubArray(nums: IntArray, left: Int, rightInclusive: Int): Int {
        if (left > rightInclusive) {
            return Int.MIN_VALUE
        }
        val mid = left + (rightInclusive - left) / 2
        val leftMax = maxSubArray(nums, left, mid - 1)
        val rightMax = maxSubArray(nums, mid + 1, rightInclusive)
        val crossSum = maxCrossSum(nums, left, mid, rightInclusive)

        return maxOf(leftMax, crossSum, rightMax)
    }

    private fun maxCrossSum(nums: IntArray, left: Int, mid: Int, rightInclusive: Int): Int {
        var sum = 0
        var leftSum = 0
        for (i in mid - 1 downTo left) {
            sum += nums[i]
            leftSum = max(leftSum, sum)
        }

        sum = 0
        var rightSum = 0
        for (i in mid + 1..rightInclusive) {
            sum += nums[i]
            rightSum = max(rightSum, sum)
        }
        return leftSum + rightSum + nums[mid]
    }
}
posted @ 2020-10-17 10:27  Zhongju.copy()  阅读(109)  评论(0编辑  收藏  举报