求和最大的子数组

来源:互联网 发布:深圳西乡淘宝培训 编辑:程序博客网 时间:2024/04/28 08:15

问题描述:

一个数组中包含正数和负数,其中位置相邻的若干元素称为子数组,求总和最大的子数组,举例{1, -2, 3, 10, -4, 7, 2, -5},最大子数组是{3, 10, -4, 7, 2},要求时间复杂度为O(n)

问题分析:

首先考虑临界情况,如果数组中全是负数,则直接返回数组中的最大负数即可,

现在考虑一般情况,即数组中至少存在一个正数,因为时间复杂度要求为 O(n) ,即要求只能对数组进行一次遍历,我们假定从左往右遍历,

我们先简化考虑,将原数组中所有相邻的正数相加,所有相邻的负数也相加,例如:

{-1, -2 ,5 ,-2, 10,16, -2 , -4, -9, 1, 2},简化后就变成{-3, 5 , -2 , 26, -15, 3}

可以看到,简化后的数组正负数是间隔着排列的,现在分析简化后的数组,如下是以从左往右遍历的第一个和第二个正数为端点的子数组的几种可能情况:

1)   5,  -2, 26  ---负数小于两端的正数

2)   5,  -12, 26 --负数大于左边的正数,小于右边的正数

3)  26,  -12, 5 --负数小于左边的正数,大于右边的正数

4)  12,  -26, 5 --负数大于两端的正数

对于上述1),当遍历到第二个正数时,总和最大的子数组是可能包括这个子数组{5, -2, 26}的,并且总和是 5 + (-2)+ 26 = 29,

对于上述2),当遍历到第二个正数时,总和最大的子数组肯定不可能包括第一个正数了,所以可以放心的把第一个正数抛弃,所以子数组为{26},总和是26,

对于上述3),当遍历到第二个正数时,总和最大的子数组肯定包括第一个正数,第二个正数可能包括,因为咱们不知道第二个正数后面还有没有更大的正数,所以

这里既要保留当前总和最大的子数组{26}、最大值26,又要保留子数组{26, -12, 5}、总和 26 + (-12)+ 5 = 19,

对于上述4),当遍历到第二个正数时,总和最大的子数组要么就是子数组{12},要么就包括第二个正数{5},理由同上,因为有可能第三个正数很大,


从上分析可以得出,至少设置4个临时变量:当前总和最大的子数组int[] maxArr及其总和 int max,有可能总和最大的子数组int[] currMaxArr及其总和 int currMax,

这样遍历到第三个正数时,将currMax作为左边的端点,第三个正数作为右边的端点,作上述同样的判断,并将计算得出的最大值和max比较,如果比max大,就替换max和maxArr的值。后续就是不断的重复此过程,一直到数组的最后一个元素。

代码如下:

public static List<Integer> test(int[] n){int max = 0;//子数组和的最大值List<Integer> maxArr = new ArrayList<Integer>();//最大值对应的子数组int currMax = 0;//当前计算的最大值List<Integer> currMaxArr = new ArrayList<Integer>();//当前最大值对应的子数组maxArr.add(n[0]);for(int i=0; i<n.length; i++){if(currMax < 0){currMax = n[i];currMaxArr.removeAll(currMaxArr);currMaxArr.add(n[i]);}else{currMax += n[i];currMaxArr.add(n[i]);}if(max < currMax){max = currMax;maxArr.removeAll(maxArr);maxArr.addAll(currMaxArr);}}return maxArr;}


原创粉丝点击