要点:滑动窗口、动态规划
描述
1 | 给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。 |
关键点
方法一、前缀和+暴力解
求序列和可以用前缀和(prefixSum
) 来优化,给定子序列的首尾位置(l, r),
那么序列和 subarraySum=prefixSum[r] - prefixSum[l - 1];
用一个全局变量maxSum
, 比较每次求解的子序列和,maxSum = max(maxSum, subarraySum)
.
1 | /** |
- 203/203 cases passed (172 ms)
- Your runtime beats 7.92 % of javascript submissions
- Your memory usage beats 76.04 % of javascript submissions (39.2 MB)
时间复杂度:$O(N ^ 2)$, 其中 N 是数组长度
空间复杂度:$O(N)$
方法二、优化前缀和
我们定义函数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,2,...,i-1
,中的最小值即可。 因此我们需要 用一个变量来维护这个最小值,还需要一个变量维护最大值。
1 | /** |
- 203/203 cases passed (72 ms)
- Your runtime beats 84.33 % of javascript submissions
- Your memory usage beats 85.98 % of javascript submissions (39.1 MB)
时间复杂度:$O(N)$, 其中 N 是数组长度
空间复杂度:$O(1)$
方法三、分治法
我们把数组nums
以中间位置(m
)分为左(left
)右(right
)两部分. 那么有, left = nums[0]...nums[m - 1]
和 right = nums[m + 1]...nums[n-1]
最大子序列和的位置有以下三种情况:
- 考虑中间元素
nums[m]
, 跨越左右两部分,这里从中间元素开始,往左求出后缀最大,往右求出前缀最大, 保持连续性。 - 不考虑中间元素,最大子序列和出现在左半部分,递归求解左边部分最大子序列和
- 不考虑中间元素,最大子序列和出现在右半部分,递归求解右边部分最大子序列和
分别求出三种情况下最大子序列和,三者中最大值即为最大子序列和。
1 | function helper(list, m, n) { |
- 203/203 cases passed (76 ms)
- Your runtime beats 70.48 % of javascript submissions
- Your memory usage beats 22.56 % of javascript submissions (39.5 MB)
时间复杂度:$O(NlogN)$, 其中 N 是数组长度
空间复杂度:$O(logN)$
方法四、动态规划
1 | function LSS(list) { |
时间复杂度:$O(N)$, 其中 N 是数组长度
空间复杂度:$O(1)$
Tips: Please indicate the source and original author when reprinting or quoting this article.