求子数组的最大和(数组)
题目:
输入一个整形数组,数组里有正数也有负数。数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。求所有子数组的和的最大值。要求时间复杂度为
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