求子数组的最大和(数组)
题目:
输入一个整形数组,数组里有正数也有负数。数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。求所有子数组的和的最大值。要求时间复杂度为O(n)。
例如输入的数组为1, -2, 3, 10, -4, 7, 2, -5,和最大的子数组为3, 10, -4, 7, 2,因此输出为该子数组的和18。
解题思路:
我觉得用户关心的数据有两个,一个是和最大子数据的和具体值,还有就是该子数据具体有哪些元素,所以我们可以封装一个子数据类SubArray,成员有数组开始索引,结束索引,以及子数据的和。
代码如下:
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测试
public class MaxSubArrayTest {
@Test
public void testMaxOneSizeArray() {
Random random = new Random();
int count = 100000;
int maxValue = 0;
int maxValueIx = 0;
MaxSubArray sub = new MaxSubArray(count);
for (int i = 0; i < count; i++){
int value = random.nextInt();
if (value > maxValue){
maxValue = value;
maxValueIx = i;
}
sub.add(value);
}
SubArray maxArray = sub.maxSubArray(1);
assertEquals(maxArray.getStartIx(), maxArray.getEndIx());
assertEquals(maxArray.getStartIx(), maxValueIx);
assertEquals(maxArray.getSum(), maxValue);
}
@Test
public void testMaxSizeArray(){
int[] valueArr = {1, -2, 3, 10, -4, 7, 2, -5};
int count = valueArr.length;
MaxSubArray sub = new MaxSubArray(count);
for (int i = 0; i < count; i++){
sub.add(valueArr[i]);
}
SubArray maxArray = sub.maxSubArray(5);
// 最大的5个字数组为3,10,-4,7,2
assertEquals(maxArray.getStartIx(), 2);
assertEquals(maxArray.getEndIx(), 6);
assertEquals(maxArray.getSum(), 18);
}
@Test
public void testMultiMaxSizeArray(){
Random random = new Random();
int count = 100;
int size = random.nextInt(count / 2);
while (size <= 0){
size = random.nextInt(count / 2);
}
long maxValue = 0;
MaxSubArray sub = new MaxSubArray(count);
int maxValueStartIx = count / 2 - size;
int i = 0;
for (; i < maxValueStartIx; i++){
int value = random.nextInt(1000);
sub.add(value);
}
int maxValueEndIx = maxValueStartIx + size - 1;
for (; i <= maxValueEndIx; i++){
int value = random.nextInt(10 * 1000);
while (value < 1 * 1000){
value = random.nextInt();
}
sub.add(value);
maxValue += value;
}
for (; i < count; i++){
int value = random.nextInt(1000);
sub.add(value);
}
SubArray maxArray = sub.maxSubArray(size);
assertEquals(maxArray.getStartIx(), maxValueStartIx);
assertEquals(maxArray.getEndIx(), maxValueEndIx);
assertEquals(maxArray.getSum(), maxValue);
}
@Test
public void testOneMaxArray(){
}
@Test
public void testMaxArray(){
int[] valueArr = {1, -2, 3, 10, -4, 7, 2, -5, 6};
int count = valueArr.length;
MaxSubArray sub = new MaxSubArray(count);
for (int i = 0; i < count; i++){
sub.add(valueArr[i]);
}
SubArray maxArray = sub.maxSubArray();
// 最大的子数组为3,10,-4,7,2
assertEquals(maxArray.getStartIx(), 2);
assertEquals(maxArray.getEndIx(), 8);
assertEquals(maxArray.getSum(), 19);
}
}