Java實現:求子數組的最大和(數組),java數組
求子數組的最大和(數組)
題目:
輸入一個整形數組,數組裡有正數也有負數。數組中連續的一個或多個整數組成一個子數組,每個子數組都有一個和。求所有子數組的和的最大值。要求時間複雜度為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測試
public class MaxSubArrayTest {@Testpublic 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);}@Testpublic 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,2assertEquals(maxArray.getStartIx(), 2);assertEquals(maxArray.getEndIx(), 6);assertEquals(maxArray.getSum(), 18);}@Testpublic 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);}@Testpublic void testOneMaxArray(){}@Testpublic 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,2assertEquals(maxArray.getStartIx(), 2);assertEquals(maxArray.getEndIx(), 8);assertEquals(maxArray.getSum(), 19);}}