【劍指Offer學習】【面試題31:連續子陣列的最大和】
題目:輸入一個整型陣列,數組裡有正數也有負數。陣列中一個或連續的多個整陣列成一個子陣列。求所有子陣列的和的最大值。要求時間複雜度為O(n)。
例子說明:
例如輸入的陣列為{1, -2, 3, 10, -4, 7, 2, -5},和最大的子陣列為{3, 10, -4, 7, 2}。因此輸出為該子陣列的和18 。
解題思路:
解法一:舉例分析陣列的規律。
我們試著從頭到尾逐個累加示例陣列中的每個數字。初始化和為0。第一步加上第一個數字1, 此時和為1。接下來第二步加上數字-2,和就變成了-1。第三步刷上數字3。我們注意到由於此前累計的和是-1 ,小於0,那如果用-1 加上3 ,得到的和是2 , 比3 本身還小。也就是說從第一個數字開始的子陣列的和會小於從第三個數字開始的子陣列的和。因此我們不用考慮從第一個數字開始的子陣列,之前累計的和也被拋棄。
我們從第三個數字重新開始累加,此時得到的和是3 。接下來第四步加10,得到和為13 。第五步加上-4, 和為9。我們發現由於-4 是一個負數,因此累加-4 之後得到的和比原來的和還要小。因此我們要把之前得到的和13 儲存下來,它有可能是最大的子陣列的和。第六步加上數字.7,9 加7 的結果是16,此時和比之前最大的和13 還要大, 把最大的子陣列的和由13更新為16。第七步加上2,累加得到的和為18,同時我們也要更新最大子陣列的和。第八步加上最後一個數字-5,由於得到的和為13 ,小於此前最大的和18,因此最終最大的子陣列的和為18 ,對應的子陣列是{3, 10, -4, 7, 2}。
解法二: 應用動態歸劃法。
可以用動態規劃的思想來分析這個問題。如果用函式f(i)表示以第i個數字結尾的子陣列的最大和,那麼我們需要求出max[f(i)],其中0 <= i < n。我們可用如下邊歸公式求f(i):
這個公式的意義:當以第i-1 個數字結尾的子陣列中所有數字的和小於0時,如果把這個負數與第i個數累加,得到的結果比第i個數字本身還要小,所以這種情況下以第i個數字結尾的子陣列就是第i個數字本身。如果以第i-1 個數字結尾的子陣列中所有數字的和大於0 ,與第i 個數字累加就得到以第i個數字結尾的子陣列中所有數字的和。
本題採用第一種實現方式
程式碼實現:
public class Test31 {
/**
* 題目2 輸入一個整型陣列,數組裡有正數也有負數。陣列中一個或連
* 續的多個整陣列成一個子陣列。求所有子陣列的和的最大值。要求時間複雜度為O(n)。
*
* @param arr 輸入陣列
* @return 最大的連續子陣列和
*/
public static int findGreatestSumOfSubArray(int[] arr) {
// 引數校驗
if (arr == null || arr.length < 1) {
throw new IllegalArgumentException("Array must contain an element");
}
// 記錄最大的子陣列和,開始時是最小的整數
int max = Integer.MIN_VALUE;
// 當前的和
int curMax = 0;
// 陣列遍歷
for (int i : arr) {
// 如果當前和小於等於0,就重新設定當前和
if (curMax <= 0) {
curMax = i;
}
// 如果當前和大於0,累加當前和
else {
curMax += i;
}
// 更新記錄到的最在的子陣列和
if (max < curMax) {
max = curMax;
}
}
return max;
}
public static void main(String[] args) {
int[] data = {1, -2, 3, 10, -4, 7, 2, -5};
int[] data2 = {-2, -8, -1, -5, -9};
int[] data3 = {2, 8, 1, 5, 9};
System.out.println(findGreatestSumOfSubArray(data));
System.out.println(findGreatestSumOfSubArray(data2));
System.out.println(findGreatestSumOfSubArray(data3));
}
}