最大子序列和Kadane算法及其证明
问题描述
力扣上有一个经典的题目53. Maximum Subarry,题目描述如下。
Given an integer array nums, find the subarray with the largest sum, and return its sum.
思路
- brute force: 双重循环,第一层循环遍历选择每一个点作为subarray的起点,第二层循环遍历选择之后每一个点作为subarray的终点。这个算法时间复杂度是 O ( N 2 ) O(N^2) O(N2)。明显不理想。
- 优化思路:如何一次循环就可以找到最大的subarray呢?处理这个问题的关键就在于,取什么点作为起点。
为什么取什么点作为终点不是问题的关键呢?因为在一次循环中,每一个点都会被作为终点使用到。
思考如何取起点:假设我们当前取的subarray是 A r r a y [ i , j ] Array[i, j] Array[i,j](下标从i到j-1的数组)。如果将终点向后移动一位,得到 A r r a y [ i , j + 1 ] Array[i, j+1] Array[i,j+1],什么情况下会这个子数组是一定不考虑的答案呢?那就是当 A r r a y [ i , j ] < 0 Array[i, j]<0 Array[i,j]<0。因为 A r r a y [ i , j ] + n u m s [ j ] < n u m s [ j ] Array[i, j] + nums[j]<nums[j] Array[i,j]+nums[j]<nums[j]。所以我们就从j开始作为起点,然后向后依次取终点计算和。
Kadane’s Algorithm
这也就是Kadane算法。维基百科中给出的代码是:
def max_subarray(numbers):
"""Find the largest sum of any contiguous subarray."""
best_sum = 0
current_sum = 0
for x in numbers:
current_sum = max(x, current_sum + x)
best_sum = max(best_sum, current_sum)
return best_sum
我们可能有一个疑问:为什么 A r r a y [ i , j ] < 0 Array[i, j] < 0 Array[i,j]<0那就把所有i到j-1都不作为起点的考虑呢?
给出简单证明:
假设
A
r
r
a
y
[
i
,
j
]
<
0
Array[i, j] < 0
Array[i,j]<0且有
A
r
r
a
y
[
k
,
j
]
>
0
,
i
<
k
<
j
Array[k, j] > 0, i<k<j
Array[k,j]>0,i<k<j。
那么,
A
r
r
a
y
[
i
,
k
]
<
0
Array[i, k]<0
Array[i,k]<0。
但是任何和小于0的subarray都不会和后面的subarray合并。
所以这个假设不成立。
代码
class Solution {
public int maxSubArray(int[] nums) {
int maxSubarray = Integer.MIN_VALUE;
for (int i = 0; i < nums.length; i++) {
int currentSubarray = 0;
for (int j = i; j < nums.length; j++) {
currentSubarray += nums[j];
maxSubarray = Math.max(maxSubarray, currentSubarray);
}
}
return maxSubarray;
}
}
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
max_subarray = -math.inf
for i in range(len(nums)):
current_subarray = 0
for j in range(i, len(nums)):
current_subarray += nums[j]
max_subarray = max(max_subarray, current_subarray)
return max_subarray
Time Complexity:
O
(
N
)
O(N)
O(N)
Space Complexity:
O
(
1
)
O(1)
O(1)