介绍
计算机历史上有一个非常经典的问题叫做Maximum-Subarray-Problem, 求数组的最大子序列,要求该子序列和为最大值,子序列应当连续。
比如 [−2, 1, −3, 4, −1, 2, 1, −5, 4], 最大应该子序列为 [4, −1, 2, 1]。
暴力的解法是算出所有的子序列,然后求和,该方法的时间复杂度为O(n^2).
Kadane’s Algorithm
Kadane’s Algorithm是求解该类问题的一个通法,原理是利用Dynamic Programming保存所有以i结尾的子序列的最大长度,dp公式为:
如果以i - 1为结尾的子序列最大值为负,那么不管怎么样,dp[i]就应该从头开始计数,因为前面的子序列已经不再有影响了(dp[i - 1] + nums[i]必然小于nums[i])
Leetcode里有两道经典的题目,用到了这个算法,分别为Leetcode53和Leetcode1186,Leetcode 53是这一算法的直接运用,代码如下:1
2
3
4
5
6
7
8
9
10public int maxSubArray(int[] nums) {
int[] dp = new int[nums.length];
dp[0] = nums[0];
int res = dp[0];
for(int i = 1; i < nums.length; i++){
dp[i] = Math.max(dp[i - 1], 0) + nums[i];
res = Math.max(dp[i], res);
}
return res;
}
Leetcode1186是一个变式,基本思路不变,不过我们可以选择是否删除删除一个元素,显然,如果遇到负数,我们可以考虑将其删除,然后计算两头,对于第i个数字,如果它是负数,则计算以i - 1为结尾的最大子序列和以i + 1为开头的最大子序列,我们分别用dp1,dp2存储这两种状态,原理依旧是Kadane’s Algorithm
1 | public int maximumSum(int[] arr) { |
总结
为什么突然写这个? 因为做到1186卡了挺久,当然也有读错题的缘故,不过归根结底还是因为没有对这个方法进行总结,像这种以人名命名的算法还是应当总结下的 :P