题目地址(53. 最大子数组和)

题目地址(53. 最大子数组和)

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

题目描述

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

子数组 是数组中的一个连续部分。

 

示例 1:

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


示例 2:

输入:nums = [1]
输出:1


示例 3:

输入:nums = [5,4,-1,7,8]
输出:23


 

提示:

1 <= nums.length <= 105
-104 <= nums[i] <= 104

 

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

前置知识

  • 滑动窗口
  • 动态规划

公司

  • 阿里
  • 百度
  • 字节
  • 腾讯
  • bloomberg
  • linkedin
  • microsoft

思路

关键点

  • 解法一:

    • 使用分治法,将任务分解,从两个角度来观察用递归实现的分治法。
    1. 从上到下(由大到小):
      我们把数组nums以中间位置(m)分为左(left)右(right)两部分. 那么有, left = nums[0]…nums[m - 1] 和 right = nums[m + 1]…nums[n-1]
      最大子序列和的位置有以下三种情况:

      • 考虑中间元素nums[m], 跨越左右两部分,这里从中间元素开始,往左求出后缀最大,往右求出前缀最大, 保持连续性。
      • 不考虑中间元素,最大子序列和出现在左半部分,递归求解左边部分最大子序列和
      • 不考虑中间元素,最大子序列和出现在右半部分,递归求解右边部分最大子序列和
        分别求出三种情况下最大子序列和,三者中最大值即为最大子序列和。
    2. 从小到大:
      从上往下(大数组如何划分为小数组):

      左边数组中间值右边数组中间值合并前缀和、后缀和结果
      -inf中间值-inf中间值
      -inf第一个值第二个值max(中间值,中间值+第二个值)
      第一个元素中间值第三个元素max(中间值,中间值+第一个值,中间值+第三个值)

    该解法每次将数组分为三部分,一次计算中间值+前后缀和(两个循环实现),一次计算左边最大子序列,一次计算右边子序列。
    递归的判断边界:至少处理一个节点;如果l>r,返回-inf。
    递归划分条件:helper(l,mid-1) ; helpder(mid+1,r)
    在这里插入图片描述

  • 解法二:

动态规划的难点在于找到状态转移方程,

dp[i] - 表示到当前位置 i 的最大子序列和

状态转移方程为:dp[i] = max(dp[i - 1] + nums[i], nums[i])

初始化:dp[0] = nums[0]

从状态转移方程中,我们只关注前一个状态的值,所以不需要开一个数组记录位置所有子序列和,只需要两个变量,

currMaxSum - 累计最大和到当前位置i

maxSum - 全局最大子序列和:

currMaxSum = max(currMaxSum + nums[i], nums[i])
maxSum = max(currMaxSum, maxSum)
在这里插入图片描述

代码

  • 语言支持:Python3

Python3 Code:
解法一:


# import sys
class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        return self.helper(nums, 0, len(nums)-1)
    
    def helper(self, nums, l, r):
        if l>r:
            return -sys.maxsize
        mid = (l+r) // 2
        left = self.helper(nums, l, mid-1)
        right = self.helper(nums, mid+1, r)
        # 默认左边后缀和以及右边前缀和都为0
        left_suffix_max_sum = right_prefix_max_sum = 0
        sum = 0
        for i in reversed(range(l,mid)):
            sum += nums[i]
            left_suffix_max_sum = max(left_suffix_max_sum, sum)
        sum = 0
        for i in range(mid+1, r+1):
            sum += nums[i]
            right_prefix_max_sum = max(right_prefix_max_sum, sum)
        cross_max_sum = left_suffix_max_sum + right_prefix_max_sum + nums[mid]
        return max(cross_max_sum,left, right)

复杂度分析

令 n 为数组长度。

  • 时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)
  • 空间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)

解法二:


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

复杂度分析

令 n 为数组长度。

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)

参考

  1. 解题思路
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值