【leetcode-Python】-Dynamic Programming -53. Maximum Subarray(最大和连续子数组问题)

题目链接

https://leetcode.com/problems/maximum-subarray/

题目描述

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

示例

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

解决思路

这个问题将采用动态规划算法来解决。首先参考《剑指offer》,解答这样一个问题:什么题目可以考虑用动态规划算法去求解。

如果题目满足以下三个特征:

1、求一个问题的最优解(通常是求最大值或最小值);

2、该问题的最优解依赖各个子问题的最优解,子问题的最优解组合起来能够得到整个问题的最优解(最优子结构);

3、该问题能被分解为若干个子问题,这些子问题之间还有相互重叠的更小的子问题

那么我们就可以考虑用动态规划来解决这个问题。

由于子问题会互相重叠,为了避免重复求解子问题,可以按照从下往上的顺序先求小问题的最优解并存储下来,再在此基础上求大问题的最优解。做到从上往下分析问题,从下往上求解问题。

【动态规划理论】:一篇文章带你彻底搞懂最优子结构、无后效性和重复子问题这篇文章定义了“一个模型三个特征”,将动态规划算法解决问题的模型概括为“多阶段决策最优解模型”,在解决问题过程中需要经历多个决策阶段,每个决策阶段对应一组状态。算法目的就是找到一组决策序列,经过这组决策序列,能够产生最终期望求解的最优值。

同时,对于是适合用动态规划解决的问题,文章定义了三个特征:最优子结构、无后效性、重复子问题。

1、最优子结构

最优子结构指问题的最优解包含子问题的最优解,即可以通过子问题的最优解,推导出问题的最优解。

2、无后效性

无后效性有两层含义,第一层含义是,在推导后面阶段状态的时候,我们只关心前面阶段的状态值,不关心这个状态是怎么一步步推导出来的。第二层含义是,某阶段状态一旦确定,就不受之后阶段的决策影响。无后效性是一个非常“宽松”的要求。只要满足前面提到的动态规划问题模型,其实基本上都会满足无后效性。

3、重复子问题

不同的决策序列,到达某个相同的阶段时,可能会产生重复的状态。

确定问题可以由动态规划算法解决后,首先寻找最优子结构(可以先分析问题中的递推关系,通过寻找规律得到最优子结构)。对于最大子数组和的问题,最优子结构可以定义为“以nums[i]为结尾的连续子数组的最大和”,并用f(i)来表示。如果已知f(i-1)即以nums[i-1]为结尾的连续子数组的最大和,f(i)有两个取值:f(i-1)+nums[i]和nums[i]。要看f(i-1)是否为正数,如果为正数,则f(i)取值为f(i-1)+nums[i];如果为负数,则f(i)取值为nums[i]:f(i) = max(f(i-1)+nums[i],nums[i])。对于长度为n的数组,f(0).....f(n-1)的最大值就是最后返回的结果。

Python实现

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        for i in range(1,len(nums)):
            if nums[i-1] > 0:
                nums[i] = nums[i] + nums[i-1]
        return max(nums)
                

在代码中,可以直接用nums[i]存储f(i)的值,num[i-1]即为已经计算过的f(i-1)的值,通过判断f(i-1)是否为正数来判断是否更新nums[i]。

时间复杂度和空间复杂度

数组各元素仅访问一次,因此时间复杂度为O(n)。(python中min()、max()运算的时间复杂度为O(n))

空间复杂度为O(1)。

总结

最大子序列和问题于1977被Ulf Grenander of Brown University提出,1984年Cranegie Mellon University的Jay Kadane给出了一个线性时间复杂度的算法,被称为Kadane's Algorithm。有关子序列系列的问题可以在Kadane's Algorithm的思路、代码模板的基础上继续改进。

Kadane's Algorithm in Python

如果数组为空或数组中没有正数元素时,会返回0。

def max_subarray(numbers):
    """Find the largest sum of any contiguous subarray."""
    best_sum = 0  # or: float('-inf')
    current_sum = 0
    for x in numbers:
        #current_sum为0或正整数
        current_sum = max(0, current_sum + x) #如果current_sum+x 大于0,则更新current_sum为current_sum+x;如果小于0,说明x一定小于0,则更新current_sum为0.
        best_sum = max(best_sum, current_sum)
    return best_sum

在此基础上的变体:如果数组中没有正数元素则返回数组最大值(这种设定更常见),如果数组为空,返回-inf。

def max_subarray(numbers):
    """Find the largest sum of any contiguous subarray."""
    best_sum = float('-inf')
    current_sum = 0
    for x in numbers:
        current_sum = max(x, current_sum + x)
        best_sum = max(best_sum, current_sum)
    return best_sum

拓展题目

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值