动态规划-最大子序和

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

示例:

输入: [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

方法一:动态规划

  • 时间复杂度:O(n),其中 n 为 nums 数组的长度。我们只需要遍历一遍数组即可求得答案。
  • 空间复杂度:O(1),只需要常数空间。

维护两个变量,一个前指针的变量,一个是最大和的变量,若前一个元素大于0,则加到当前元素上

  1. 每循环一次的时候,将当前指针位置的值 和 current 相加后,和当前指针的值做对比,取两者最大的
  2. 其实current变量累加起来后就相当于一个连续子序累加的和,如果一直都是最大的话,就用上面的数组举例子
i:             0  1  2  3  4  5  6  7  8

nums:        [-2  1 -3  4 -1  2  1 -5  4]

current:      -2  1 -2  4  3  5  6  1  5       比较公式:max(current + nums[i], nums[i])  
例如nums[1]和nums[2]这两个地方,max(1 + (-3), -3)  相当于 max(-2,-3)  结果为-2,所以 current变成了-2

max:          -2  1  1  4  4  5  6  6  6
直接获取current里面出现过的最大值,就是答案了,也就是6
func maxSubArray(nums []int) int {
    //定义一个指向上一个值的指针
    current := nums[0]
    //定义 一个最大和
    max := nums[0]
    for i:=1;i<len(nums);i++ {
        //将当前指针位置的值和current相加后,和当前节点做对比,取两者最大的
        current = Max(current + nums[i], nums[i])
        max = Max(max, current )
    }
    return max
}

func Max(a,b int) int {
    if a > b {
        return a
    }
    return b
}

动态规划优化 上面的方法还能再优化一下,只维护一个max值就好了,current直接用上一个指针来nums[i-1]存储就好了

func maxSubArray(nums []int) int {
    //定义 一个最大和
    max := nums[0]
    for i:=1;i<len(nums);i++ {
        //将当前指针位置的值和前指针相加后,和当前节点做对比,取两者最大的
        nums[i] = Max(nums[i-1] + nums[i], nums[i])
        max = Max(max, nums[i]
    }
    return max
}

func Max(a, b int) int {
    if a > b {
        return a
    }
    return b
}

方法二:
贪心算法,从数组开头累加sum,sum小于零直接重置为0

  • 时间复杂度:O(n),其中 n 为 nums 数组的长度。我们只需要遍历一遍数组即可求得答案。
  • 空间复杂度:O(1),只需要常数空间。
nums                         [-2  1 -3  4 -1  2  1 -5  4]
sum(初始值0):                  -2  1 -2  4  3  5  6  1  5
max(初始值math.MinInt64):      -2  1  1  4  4  5  6  6  6
=====================================================================

func maxSubArray(nums []int) int {
    sum := 0
    max:= math.MinInt64
    for i:=0;i<len(nums);i++ {
        sum += nums[i]
        max= Max(res,sum)
        if sum < 0 {
            sum = 0
        }
    }
    return max
}
func Max(a,b int) int {
    if a > b {
        return a
    }
    return b
}

进阶:

如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。

方法三:分治法,将数组分为3个部分处理

  • 分别就是三种情况,要么最大子序列在左边,要么在右边,要么就是跨过左边和右边
  • 比较的时候,就把左序列和右序列 和 (左 + 右)3个一起对比取最大值

复杂度分析

  • 时间复杂度:O(Nlog N),分治法采用二分法,时间是log n,因为每次都需要计算横跨左右序列的和,时间复杂度为N,所以总时间复杂度为Nlog N。
  • 空间复杂度:递归会使用 O(log N) 的栈空间,故渐进空间复杂度为 O(\log n)O(logn)。
nums[] int                                     -2  1 -3  4 -1  2  1 -5  4
                                                       # l=4 r=4 m=6 max=6 
                              [-2  1 -3  4]                                 [-1 2 1 -5 4]
                           # l=1 r=4 m=2 max=4                             # l=2 r=4 m=3 max=4  
                            [-2 1]    [-3 4]                              [-1 2]    [1 -5 4]        
                          [-2]  [1]   [-3] [4]                        [-1] [2]        [1] [-5 4]   
                                                                                            # l=1 r=4 m=0 max=4 
                                                                                              [-5] [4]  
//分治法,用二分法分别在两边找出最大的连续子序,数据结构为线段树
func maxSubArray(nums []int) int {
    return help(nums, 0, len(nums) - 1);
}
func help(nums [] int,l, r int) int {
    //当左指针等于右指针的时候,就是已经递归到最后面了,只有一个值
    if (l == r) {
        return nums[l]
    }
    m := (l + r) >> 1
    leftSum := help(nums, l, m)
    rightSum := help(nums, m + 1, r)
    midSum := getCrossMax(nums, l, m, r)

    return max(max(leftSum,rightSum),midSum)
}

//获取横跨左右序列的最大连续序列
func getCrossMax(nums []int,left,mid,right int) int {
    //获取左序列最大连续子序列的和
    leftSum := math.MinInt64
    sum := 0
    for i := mid; i >= left; i-- {
        //贪心算法获取最大和
        sum += nums[i]
        leftSum = max(leftSum,sum)
    }

     //获取右序列最大连续子序列的和
    rightSum := math.MinInt64
    sum = 0
    for i := mid + 1; i <= right; i++ {
        //贪心算法获取最大和
        sum += nums[i]
        rightSum = max(rightSum,sum)
    }
    return (leftSum + rightSum)
}

func max(x, y int) int {
    if x > y {
        return x
    }
    return y
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。