最大子数组问题(python年度更新系列)
**输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。
要求时间复杂度为O(n)。
示例1:
输入: nums = [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
本题用来解释分治法的思想,显然解决这个问题的最优解是采取贪心的思想,时间复杂度为线性级别O(n),因为每次必有一个元素作为最大子数组的队尾,因此我们选取num[i]作为队尾,依次加上num[i-1],num[i-2],并将其记录在一个maxsum中,若更大则更新。
贪心思想:
def maxSubArray(self, nums):
maxsum = num[0]
#从第一个num开始遍历
for i in range(1:len(nums)):
if nums[i] <= nums[i] + nums[i-1]:
nums[i] = nums[i] + nums[i-1]
maxsum = max(maxsum,nums[i])
return maxsum
-2 1 -3 4 -1 2 1 -5 4
如果-2+1>1,则更新,否则继续向后遍历,每次更新的num[i]既是该num的最大子数组和
1 -3 4 -1 2 1 -5 4
检查是否需要更新,并向后遍历:
-2 4 -1 2 1 -5 4
更新:
4 -1 2 1 -5 4
更新:
3 2 1 -5 4
更新:
5 1 -5 4
更新:
6 -5 4
更新:
1 4
更新:
5
简洁的写法(依然是贪心):
def maxSubArray(self, nums):
for i in range(1, len(nums)):
nums[i] = max([nums[i] + nums[i-1], nums[i]])
return max(nums)
这种写法显然是最优解。下面我们来编写另外一种方法:分治法
分治思想:
我们将数组分为两个规模几乎相等的数组,A[left,mid],A[mid+1,right]去寻找最大子数组,那么最大子数组必然是下面的情况之一:
1·完全位于A[left,mid]中。
2·完全位于A[mid+1,right]中。
3·跨越mid位于A[i,j]中,left ≤ i ≤ mid ≤ j ≤ right。
情况1、2是相同的子问题,规模为先决问题的1/2,情况3则需要单独解决,以mid,mid+1为处理点,分别找到mid以左的最大值和mid+1以右的最大值,max1,max2,并相加,最后返回三种情况里的最大值即可。你看这是不是很像分类讨论。
class Solution(object):
#给出左右的下标index
left = 0
right = len(nums)-1
def maxSubSum(nums,left,right):
#找出mid+1以右的最大值
def Aright(nums,mid,right):
sum = 0
max_sum = -111111
while mid-1 <= self.right:
sum = sum + nums[mid]
mid = mid + 1
if sum >= max_sum:
max_sum = sum
return max_sum
#找出mid以左的最大值
def Aleft(nums,left,mid):
sum = 0
max_sum = -111111
i = mid
while mid >= 0:
sum = sum + nums[mid]
mid = mid - 1
if sum >= max_sum:
max_sum = sum
return max_sum
#定义mid
mid = (left + right)/2
#找出左边的最大子数组和,子问题直接递归
leftsum = maxSubSum(nums,left,mid)
#找出右边的最大子数组和,子问题直接递归
rightsum = maxSubSum(nums,mid+1,right)
#找出跨mid的最大子数组和
#左边
s1 = Aleft(nums,left,mid)
#右边
s2 = Aright(nums,mid+1,right)
#返回三者的最大值
return max(leftsum,max(rightsum,(s1+s2)/2))
return maxSubSum(nums,left,right)
这种写法若有n个结点,则有lgn的层数。
每层的时间的代价都是n,(子问题的分解所以解决的时间为1n,2n/2…)。
那么时间复杂度为O(nlgn)显然没有贪心的策略好,但是显然优于暴力算法的O(n²)。