求子数组的最大和(数组)
题目:
输入一个整形数组,数组里有正数也有负数。数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。求所有子数组的和的最大值。要求时间复杂度为
O(n)。
例如输入的数组为 1, -2, 3, 10, -4, 7, 2, -5,和最大的子数组为 3, 10, -4, 7, 2,因此输出为该子数组的和 18。
解题思路:
我觉得用户关心的的数据有两个,一个是子数组的和,还有一个是子数组具体有哪些元素,所以我们可以封装一个子数组类SubArray,成员有子数据开始索引,结束索引,以及子数组和。
对于此题,我看其他博客解答时传入了一个子数组长度size的入参,求的所有长度为size的子数组的最大值。但是上面看题目中,没有任何关于该size入参的说明。我们还是提供这两种方法的解题方法。
第一种,传入指定数组长度size。这种方法实现比较简单。首先我们有一个最大子数组SubArray的变量maxArray,然后我们再初始化一个大小为size的SubArray对象,命名为currentArray。当我们遍历用户元素时,先从currentArray中移除第一个元素,再放入用户元素,当新的currentArray的和大于maxArray时,更新maxArray即可。
第二种,没有传入数组长度size。我们只要确保索引index之前的数据之和加上index索引数据>0,那么index索引对应最大值是有贡献的。当index之前的数据之和加上index索引之和<0,则表示index之前的数据(包括index)都是无用的,删除即可。当得到得到上面数组时,我们需要反序循环一次,将队尾的负值移除,确保子数组的和是最大。
代码如下:
public class MaxSubArray { private List<Integer> valueList = null; public MaxSubArray(){ valueList = new ArrayList<Integer>(); } public MaxSubArray(int size){ valueList = new ArrayList<Integer>(size); } public void add(int value){ valueList.add(value); } public SubArray maxSubArray(int size){ if (valueList.isEmpty()){ return null; } // 初始化当前操作的数组和最大数组 int currentSize = Math.min(valueList.size(), size); SubArray currentArray = new SubArray(0, 0, 0); for (int i = 0; i < currentSize; i++){ currentArray.endIx = i; currentArray.sum += valueList.get(i); } SubArray maxArray = new SubArray(currentArray); // 计算 for (int i = size; i < valueList.size(); i++){ // 如果当前数组没有预留一个空位,则从头移除元素,保证预留一个空位 if (currentArray.endIx - currentArray.startIx + 1 >= size){ currentArray.sum -= valueList.get(currentArray.startIx); currentArray.startIx++; } currentArray.endIx = i; currentArray.sum += valueList.get(i); if (currentArray.sum > maxArray.sum){ maxArray.copy(currentArray); } } return maxArray; } public SubArray maxSubArray(){ if (valueList.isEmpty()){ return null; } // 从前往后循环,只要第i位置之前的总和是小于第i位置值,则包括第i位置的子数组丢弃。 SubArray lastMaxArray = new SubArray(0, 0, valueList.get(0)); SubArray resultArray = new SubArray(lastMaxArray); for (int i = 1; i < valueList.size(); i++){ int value = valueList.get(i); if (resultArray == null){ resultArray = new SubArray(i, i, value); continue; } if (resultArray.sum + value < 0){ if (resultArray.sum > lastMaxArray.sum){ lastMaxArray.copy(resultArray); } resultArray = null; continue; } resultArray.endIx=i; resultArray.sum += valueList.get(i); } // 从后向前循环,只要第i位置之后的总和是小于第i位置值,则包括第i位置在内的之后数组丢弃。 for (int i = resultArray.endIx; i >= resultArray.startIx; i--){ int value = valueList.get(i); if (value < 0){ resultArray.endIx = i - 1; resultArray.sum -= value; continue; } else { break; } } return resultArray; } public class SubArray{ private int startIx; private int endIx; private long sum; public SubArray(SubArray copy){ copy(copy); } public SubArray(int startIx, int endIx, long sum){ this.startIx = startIx; this.endIx = endIx; this.sum = sum; } public void copy(SubArray copy){ this.startIx = copy.startIx; this.endIx = copy.endIx; this.sum = copy.sum; } public int getStartIx(){ return this.startIx; } public int getEndIx(){ return this.endIx; } public long getSum(){ return this.sum; } } }
先去吃饭,等会来写Junit测试。
原文地址:http://blog.csdn.net/fangchao2061/article/details/43370445