zoukankan      html  css  js  c++  java
  • [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]
        }
    }
    
  • 相关阅读:
    NOP(4) default
    NOP(三) ASP.NET Application Life Cycle
    About the IoC
    开园庆祝!
    js 添加/删除数组开头/结尾元素
    JavaScript String.prototype.slice()
    JavaScript Array.prototype.splice()方法的使用
    js Map
    js Set
    Bruteforce Algorithm [HDU 3221]
  • 原文地址:https://www.cnblogs.com/zhongju/p/13830128.html
Copyright © 2011-2022 走看看