53. Maximum Subarray 最大子串和问题

LeetCode传送门

    本题为经典的面试笔试题,要求给一个数字序列,求其最大子串和,注意需要与最大子序列和的区别,子串要求连续,而子序列只要数字的顺序符合即可,不要求连续。

    解题思路主要有一下三种:

     1.暴力解法:

        这里不再啰嗦,算法复杂度为O(n2),LeetCode中超时无法 AC。

    2.分治法

        思路:将大问题转化为小问题,然后解决。

        以具体例子说明,假设数字序列为:nums = [-2,1,-3,4,-1,2,1,-5,4]

        想要求 nums 中 0~8 位置的最大子串和,我们可以将其分为两部分,即 0~4 和 5~8。这样,0~8 的最长子串和来自三个部分:

        (1) 0 ~ 4 的最长子串和

        (2) 5 ~ 8 的最长子串和

        (3) 包含 nums[4] 的 0 ~ 8 最长子串和

        没想明白的同学可以用笔画一下辅助理解

        按此此路,给上代码:

class Solution(object):
    def feizhi(self, nums, left, right):
        if left == right:
            #print("base case:", left)
            return nums[left]
        middle = (left + right) // 2

        leftMax = self.feizhi(nums, left, middle)
        rightMax = self.feizhi(nums, middle + 1, right)

        i = middle
        sum = 0
        leftSum = nums[middle]
        while i >= left:
            sum += nums[i]
            leftSum = max(leftSum, sum)
            i -= 1

        j = middle + 1
        sum = 0
        rightSum = nums[middle + 1]
        while j <= right:
            sum += nums[j]
            rightSum = max(rightSum, sum)
            j += 1

        middleMax = leftSum + rightSum

        #print(left, right, leftMax, rightMax, middleMax)
        return max(leftMax, rightMax, middleMax)


    def maxSubArray(self, nums):
        if len(nums) == 0:
            return 0
        return self.feizhi(nums, 0, len(nums) - 1)

A = Solution()
result = A.maxSubArray([-2])
print(result)

    将注释去掉后,可以看到每一次分治后的结果。

    算法复杂度为O(nlogn),因为需要分治 logn 次,每次计算 middleMax 需要 O(n)。 


3. Kadane 算法

    说明Kadane算法的正确性,需要两个结论。


    (1)首先,对于array[1...n],如果array[i...j]就是满足和最大的子串,那么对于任何k(i<=k<=j),我们有array[i...k]的和大于0。因为如果存在k使得array[i...k]的和小于0,那么我们就有array[k+1...j]的和大于array[i...j],这与我们假设的array[i...j]就是array中和最大子串矛盾。


    (2)其次,我们可以将数组从左到右分割为若干子串,使得除了最后一个子串之外,其余子串的各元素之和小于0,且对于所有子串array[i...j]和任意k(i<=k<j),有array[i...k]的和大于0。


    此时我们要说明的是,满足条件的和最大子串,只能是上述某个子串的前缀,而不可能跨越多个子串。我们假设array[p...q],是array的和最大子串,且array[p...q],跨越了array[i...j],array[j+1...k]。根据我们的分组方式,存在i<=m<j使得array[i...m]的和是array[i...j]中的最大值,存在j+1<=n<k使得array[j+1...n]的和是array[j+1...k]的最大值。由于array[m+1...j]使得array[i...j]的和小于0。此时我们可以比较array[i...m]和array[j+1...n],如果array[i...m]的和大于array[j+1...n]则array[i...m]>array[p...q],否array[j+1...n]>array[p...q],无论谁大,我们都可以找到比array[p...q]和更大的子串,这与我们的假设矛盾,所以满足条件的array[p...q]不可能跨越两个子串。对于跨越更多子串的情况,由于各子串的和均为负值,所以同样可以证明存在和更大的非跨越子串的存在。

    代码如下:

def max_subarray(A):
    max_ending_here = max_so_far = 0
    for x in A:
        max_ending_here = max(0, max_ending_here + x)
        max_so_far = max(max_so_far, max_ending_here)
    return max_so_far


print(max_subarray([-1, -2, -4, -4]))
    算法复杂度为 O(n),但是要注意,其要求所给的序列不能全为 负数,否则无法构造 (2)


猜你喜欢

转载自blog.csdn.net/weixin_40017590/article/details/80016224