【LeetCode刷题-简单】53. 最大子序和(python c++)

题目

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

示例:

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

进阶:

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

 

思路

这道题求解连续最大子序列和,以下从时间复杂度角度分析不同的解题思路。

解法一 - 暴力解 (暴力出奇迹, 噢耶!)

一般情况下,先从暴力解分析,然后再进行一步步的优化。

原始暴力解:(超时)

求子序列和,那么我们要知道子序列的首尾位置,然后计算首尾之间的序列和。用2个for循环可以枚举所有子序列的首尾位置。 然后用一个for循环求解序列和。这里时间复杂度太高,O(n^3).

复杂度分析

  • 时间复杂度: O(n^3) - n 是数组长度
  • 空间复杂度: O(1)

解法二 - 前缀和 + 暴力解

优化暴力解: (震惊,居然AC了)

在暴力解的基础上,用前缀和我们可以优化到暴力解O(n^2), 这里以空间换时间。 这里可以使用原数组表示prefixSum, 省空间。

求序列和可以用前缀和(prefixSum) 来优化,给定子序列的首尾位置(l, r), 那么序列和 subarraySum=prefixSum[r] - prefixSum[l - 1]; 用一个全局变量maxSum, 比较每次求解的子序列和,maxSum = max(maxSum, subarraySum).

复杂度分析

  • 时间复杂度: O(n^2) - n 是数组长度
  • 空间复杂度: O(n) - prefixSum 数组空间为n

如果用更改原数组表示前缀和数组,空间复杂度降为O(1)

但是时间复杂度还是太高,还能不能更优化。答案是可以,前缀和还可以优化到O(n).

解法三 - 优化前缀和 - from @lucifer (不太理解,有点晕)

我们定义函数 S(i) ,它的功能是计算以 0(包括 0)开始加到 i(包括 i)的值。

那么 S(j) - S(i - 1) 就等于 从 i 开始(包括 i)加到 j(包括 j)的值。

我们进一步分析,实际上我们只需要遍历一次计算出所有的 S(i), 其中 i = 0,1,2....,n-1。 然后我们再减去之前的 S(k),其中 k = 0,1,i - 1,中的最小值即可。 因此我们需要 用一个变量来维护这个最小值,还需要一个变量维护最大值。

复杂度分析

  • 时间复杂度: O(n) - n 是数组长度
  • 空间复杂度: O(1)

解法四 - 分治法

我们把数组nums以中间位置(m)分为左(left)右(right)两部分. 那么有, left = nums[0]...nums[m - 1]right = nums[m + 1]...nums[n-1]

最大子序列和的位置有以下三种情况:

  1. 考虑中间元素nums[m], 跨越左右两部分,这里从中间元素开始,往左求出后缀最大,往右求出前缀最大, 保持连续性。
  2. 不考虑中间元素,最大子序列和出现在左半部分,递归求解左边部分最大子序列和
  3. 不考虑中间元素,最大子序列和出现在右半部分,递归求解右边部分最大子序列和

分别求出三种情况下最大子序列和,三者中最大值即为最大子序列和。

举例说明,如下图:

复杂度分析

  • 时间复杂度: O(nlogn) - n 是数组长度
  • 空间复杂度: O(logn) - 因为调用栈的深度最多是logn。

解法五 - 动态规划

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

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)

如图:

复杂度分析

  • 时间复杂度: O(n) - n 是数组长度
  • 空间复杂度: O(1)

 

关键点分析

  1. 暴力解,列举所有组合子序列首尾位置的组合,求解最大的子序列和, 优化可以预先处理,得到前缀和
  2. 分治法,每次从中间位置把数组分为左右中三部分, 分别求出左右中(这里中是包括中间元素的子序列)最大和。对左右分别深度递归,三者中最大值即为当前最大子序列和。
  3. 动态规划,找到状态转移方程,求到当前位置最大和。

 

解法二 - 前缀和 + 暴力

Python

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

 

C++

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int maxnum = nums[0];
        for(int i = 0; i < nums.size(); i++)
        {
            int sumnum = 0;
            for(int j = i; j < nums.size(); j++)
            {
                sumnum += nums[j];
                maxnum = max(sumnum, maxnum);
            }
        }
        return maxnum;
    }
};

 

解法三 - 优化前缀和

Python

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        n = len(nums)
        maxSum = nums[0]
        minSum = sum = 0
        for i in range(n):
            sum += nums[i]
            maxSum = max(maxSum, sum - minSum)
            minSum = min(minSum, sum)

        return maxSum

 

C++

 

 

 

解法四 - 分治法

Python

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        n = len(nums)
        #递归终止条件
        if n == 1:
            return nums[0]
        else:
            #递归计算左半边最大子序和
            max_left = self.maxSubArray(nums[0:len(nums) // 2])
            #递归计算右半边最大子序和
            max_right = self.maxSubArray(nums[len(nums) // 2:len(nums)])
        
        #计算中间的最大子序和,从右到左计算左边的最大子序和,从左到右计算右边的最大子序和,再相加
        max_l = nums[len(nums) // 2 - 1]
        tmp = 0
        for i in range(len(nums) // 2 - 1, -1, -1):
            tmp += nums[i]
            max_l = max(tmp, max_l)
        max_r = nums[len(nums) // 2]
        tmp = 0
        for i in range(len(nums) // 2, len(nums)):
            tmp += nums[i]
            max_r = max(tmp, max_r)
        #返回三个中的最大值
        return max(max_right,max_left,max_l+max_r)

 

C++

解法五 - 动态规划

Python

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

 

C++

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int curmaxsum = 0;
        int maxsum = nums[0];
        for(int i=0; i<nums.size(); i++)
        {
            curmaxsum = max(curmaxsum + nums[i], nums[i]);
            maxsum = max(maxsum, curmaxsum);
        }
        return maxsum;
    }
};

 

 

 

 

 

 

 

 

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值