【算法导论学习笔记】最大子数组问题(分治策略)

最大子数组问题(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²)。

猜你喜欢

转载自blog.csdn.net/qq_54384621/article/details/124238100