本题为经典的面试笔试题,要求给一个数字序列,求其最大子串和,注意需要与最大子序列和的区别,子串要求连续,而子序列只要数字的顺序符合即可,不要求连续。
解题思路主要有一下三种:
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)