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]
        }
    }
    
  • 相关阅读:
    GL_TRIANGLE_FAN Vs GL_TRIANGLE_STRIP
    Color bleeding与caustics概念解析
    Two path ray tracing与Photon Mapping(粒子跟踪)
    右手定则判断法线方向
    正确使用atoi
    深入探讨透视投影坐标变换
    gluBuild2DMipmaps与glTexImage2D与glGenTexture()函数
    OpenGL纹理贴图流程
    int main( int argc, char ** argv)在VS2008中的配置的一个实例
    c++标准库中vector数组遍历时的用法
  • 原文地址:https://www.cnblogs.com/zhongju/p/13830128.html
Copyright © 2011-2022 走看看