求子数组的最大和(数组)



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

}