转载本文章请标明作者和出处
本文出自《Darwin的程序空间》
本文题目和部分解题思路来源自《剑指offer》第二版
输入一个整型数组,数组里有正数也有负数。数组中一个或连续的多个整数组成一个子数组。求所有子数组的和的最大值。要求时间复杂度为O(n)。
例如:输入的数组为{1, -2, 3, 10, -4, 7, 2, -5},和最大的子数组为{3, 10, -4, 7, 2}。因此输出为该子数组的和18。
这道题,很多人可能想到的是穷举出这个数组所有的非空子集,然后算出所有子集的和,在其中取最大值即为答案,这我们也可以称之为暴力破解法;
但是一个数组的非空子集的个数为n(n+1)/2个,这样看的话,时间复杂度怎么的也得在O(n^2),显然,这是不符合题意的;
我们仔细审题,其实题目已经给了我们提示,既然他的时间复杂度的要求是O(n),所以这就间接说明了我们要在一次遍历中搞定这个事情;双层乃至三层for循环,就不要再想了;
既然是一遍循环就能搞定,那么我们想一下,在遍历的过程中,首先是1,那么遍历到这,我们所知道的最大值就是1,然后是-2,-2加1是-1,所以最大值还是1。然后是3,那么要用-1加3么?肯定不!因为加上-1只会让3变得小,所以3开始是不会再要前面加起来为-1的子数组了,新的最长子数组应该从3为头计算,那么3和之前最大的1比,明显比1要大,所以最大值就变成3。后面是10,10加上3能变得更大,于是最大值变成了13,然后然后遇到了-4,13加上-4等于9,9没有当前最大值13大,于是最大值还是13。然后后面是7,那么7要和前面的【3,10,-4】在一起么?为什么不呢?7加上前面的9更大了啊!所以现在的最长子串是【3,10,-4,7】,最大值是16,然后后面是2,最长子串变成了【3,10,-4,7,2】,最大值变成了18,然后最后是-5,18加-5等于13,并没有18大,所以最后的结果是18;
精髓就是,每遍历到一个节点,是都要用前面的子数组,取决于,前面的子数组是否大于0,你大于0我加上你就更大,我就要你,否则,我就成为新的子数组的起点,我们没遍历一个节点就比较一下和当前最大值,最后就能求出连续子数组的最大和;
后面的结果和前面的结果息息相关,这是什么思想呢?
这两个节点先图同时指向第一个元素,max每次就负责和temp比较,一旦temp超过了它,就用temp替换掉它;因为temp可能遇到一个负数,也有可能遇到正数,在temp变成负数之前不会被贵0,而max只负责记住最大的连续和;所以temp对下一个节点负责,max对整体的结果负责;
要特别注意的是,我们要重点考虑数组全为负数的情况,此时应该返回最大的负数,做好代码的健壮性;
ps:这里笔者使用的jdk为1.8版本、python解释器为3.7
测试数据即为图示数据;
public class SubArray {
public static void main(String[] args) {
int[] arr = new int[]{1, -2, 3, 10, -4, 7, 2, -5};
int max = getMax(arr);
System.out.println(max);
}
private static int getMax(int[] arr) {
int max = arr[0];
int temp = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] + temp < 0) {
if (arr[i] + temp > max) {
max = arr[i] + temp;
}
temp = 0;
continue;
}
if (arr[i] + temp > 0) {
temp += arr[i];
}
if (temp > max) {
max = temp;
}
}
return max;
}
}
public class SubArray {
public static void main(String[] args) {
int[] arr = new int[]{1, -2, 3, 10, -4, 7, 2, -5};
int max = getMax(arr);
System.out.println(max);
}
private static int getMax(int[] arr) {
int temp = arr[0];
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
temp = Math.max(temp + arr[i], arr[i]);
max = Math.max(max, temp);
}
return max;
}
}
class Solution:
def maxSubArray(self, nums: List[int]) -> int:
max = nums[0]
sum = nums[0]
for i in range(1,len(nums)):
sum = sum + nums[i] if sum + nums[i] >= nums[i] else nums[i]
max = sum if sum > max else max
return max
其实这道题对比笔者来说是很重要的,因为这是笔者做出的第一道算法题,是来成都之后一个面试官给我出的,当时我翻遍朋友全的大神,会算法的竟然屈指可数,当我做出来这道题之后,我高兴了很久…