最大子数组和
两种解法
暴力
最直观的方法就是使用暴力来遍历区间的开头和结尾,然后对区间求和,取最大值。如果对暴力方法优化的话,可以固定区间开头为0,然后遍历区间结尾,对每个区间求和,然后通过这些区间之间做减法可以得到不同开头的区间的和,再进行比较。
举例:
nums = [1,2,3,4]
先求出区间[0,0],[0,1],[0,2],[0,3]的和,分别为1,3,6,10,然后如果想求区间[1,2]的和,就可以用区间[0,2]-[0,0],即为6-1=5。这样只需要遍历求一次和,并记录下来就可以了,时间复杂度会降低一些。
贪心
贪心策略:
首先,考虑一种极端情况,序列上的数字都是大于等于0的整数,那么最大子数组和就是该数组的总和。那么为什么会产生分歧呢?就是因为序列上有可能出现负数,所以我们来特殊考虑一下遇到负数的情况
首先,如果从头对区间求和,那么每次遇到负数(假设该负数的位置为index),就表示区间[0,index-1]的和一定要比区间[0,index]的和要大
这种情况下,我们要考虑两种情况:
首先声明一个result,保存结果,初始化为一个很小很小的值。
-
如果遇到负数了,并且区间[0,index]的和是负数,那么就要比较一下[0,index-1]的和和result的值,保留较大的。并且将开头修改到从index+1位置往后第一个不是负数的数(假设该位置为index2)。
原因:因为区间[0,index-1]和为正,区间[0,index]和为负,区间[index+1,index2-1]为负,区间[index2,index2+n]为正(n为到下一个负数位置的距离,n可能取0,即只有index2位置的这一个数),所以只要保存下区间[0,index-1]的值,然后和后面的区间[index2,index2+n]的值作比较即可。
-
如果遇到负数了,并且区间[0,index]的和是正数,那么就还有可能构成最大子数组和,所以只需要比较一下[0,index]和result,保留较大值,继续向后遍历即可。
为了代码能够简洁,我们可以再分析一下遇到正数的情况:
- 遇到正数,则还有可能构成最大子数组和,所以只需比较当前区间和和result,保留较大值,然后继续向后遍历即可。
我们发现,遇到正数和遇到负数并且区间[0,index]的操作方法一样,所以区别这三种情况可以用 [0,index]之和是否为负 为标准来区分为两种情况。
- [0,index]之和为负,则比较当前[0,index]之和和result,保留较大值,然后找到下一个不为负的值做开头,继续向后遍历。
- [0,index]之和为正,则比较当前区间之和和result,保留较大值,然后继续向后遍历。
贪心“贪”在:如果遇到区间和为负了,就立刻抛弃,然后找下一个区间。
代码如下:
def maxSubArray(nums):
count = 0 # 记录区间和
result = float('-inf') # 初始化为一个很小很小的值
for i in range(len(nums)):
count += nums[i]
if count>result: # 比较区间和 和 result,保留较大的
result = count
if count<0: # 如果区间和为负,则要找到新的起始位置,此时需要把之前统计的区间和变为0
count = 0
return result