LeetCode解题心得——最大子序和(python)

题目

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

思路

1、暴力解法

遍历所有可能的子序列,求出其中的最大和。算法时间复杂度O(n^3),空间复杂度O(1),超时

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        max_sum = -float('inf')
        for i in range(len(nums)):
            for j in range(i+1, len(nums)+1):
                array_sum = 0
                for x in nums[i:j]:
                    array_sum += x
                if array_sum > max_sum:
                    max_sum = array_sum
        return max_sum

将求和循环用矩阵乘法优化,依然超时

import numpy as np
class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        max_sum = -float('inf')
        for i in range(len(nums)):
            for j in range(i+1, len(nums)+1):
                np_array = np.array(nums[i:j]).reshape(1,-1)
                array_sum = np_array.dot(np.ones_like(np_array).T)
                if array_sum > max_sum:
                    max_sum = array_sum.item()
        return max_sum

2、前缀和

依旧两次循环,一个头一个尾。对于每一个头循环,定义sum=0,在遍历所有尾时求出头到尾所有元素之和,和最大值max_sum比较。超时

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        max_sum = -float('inf')
        for i in range(len(nums)):
            sum = 0
            for j in range(i, len(nums)):
                sum += nums[j]
                max_sum = max(max_sum,sum)
        return max_sum

优化版前缀和 算法时间复杂度O(n),空间复杂度O(1) 提交通过
首先设定minsum=0,maxsum=float(-inf)。
遍历数组,对于第i个元素,求出前i个元素之和。比较maxsum和sum-minsum的大小,更新maxsum。更新minsum。
只有当前i个元素之和,即sum,小于0时,minsum才会被更新,也就是说minsum<=0,
sum-minsum>=sum,用当前子序列减去其自身小于0的最小子序列,可以得到当前索引以内的最大子序列。

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        n = len(nums)
        maxSum = float(-inf)
        minSum = sum = 0
        for i in range(n):
            sum += nums[i] #前i个元素之和
            maxSum = max(maxSum, sum - minSum)
            minSum = min(minSum, sum) #前i个元素中从起始位置开始的最小和
            
        return maxSum

3、分治法

时间复杂度O(nlogn),空间复杂度O(logn)
将数组不断分成left,mid,right三块,mid只有一个元素,分三种情况:

  • 最大子序列和在left里
  • 最大子序列和在rigth里
  • 最大子序列和横跨left,right,由mid连接。

解决第三种情况,前两种就可以递归的解决了。对于第三种情况,分别求出左面的最大后缀和、右面的最大前缀和,返回值为最大前缀和、最大后缀和以及nums[mid]三者之和。
比较三种情况的返回值,即可求出全数组的最大子序列和。

class Solution:
    def maxSubArray(self, nums: List[int]) -> intreturn self.divid_and_conquer(nums, 0, len(nums)-1)
    
    def divid_and_conquer(self, nums, low, high):
        if low >= high:
            return nums[low]
        mid = (low + high)//2
        max_sum_left = self.divid_and_conquer(nums,low,mid-1)
        max_sum_right = self.divid_and_conquer(nums,mid+1,high)
        cross_suffix = 0
        sum = 0
        for i in range(mid-1,low-1,-1):
            sum += nums[i]
            cross_suffix = max(sum, cross_suffix)
        cross_prefix = 0
        sum = 0
        for i in range(mid+1,high+1):
            sum += nums[i]
            cross_prefix = max(sum, cross_prefix)
        cross_sum_max = cross_prefix + cross_suffix + nums[mid]
        return max(max_sum_left,cross_sum_max,max_sum_right)

4、动态规划

设max_sum_ending_curr_index[i]定义是 数组nums 中以num[i] 结尾的最大连续子串和
由于只需要知道以nums[i-1]结尾的最大连续字串和,即可得到以nums[i]结尾的最大连续字串和,所以不需要维护前面的记录,只需一个参数即可——max_sum_ending_curr_index
状态转移方程:
max_sum_ending_curr_index = max(max_sum_ending_curr_index + nums[i], nums[i])
用max_sum保存目前为止得到的最大值。
算法时间复杂度O(n),空间复杂度O(1)

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

你可能感兴趣的:(LeetCode)