最大子序列和問題是算法中一個經典問題了,不同的算法時間復雜度相差甚大。
最大子序列和
給出一組整數,求出這組數字子序列和中的最大值。
算法
-
窮舉法
這個是最容易想到的,枚舉每一個子序列的大小,記錄下最大的和返回。
public static int maxSubSum1(int[] seq) {
int maxSum = 0;
int thisSum = 0;
int len = seq.length;
for (int i = 0; i < len; i++) {
thisSum = 0; //這里要歸零
for (int j = i; j < len; j++) {
thisSum += seq[j];
if (thisSum > maxSum)
maxSum = thisSum;
}
}
return maxSum;
}
這個比較好理解,枚舉出每一個子序列,將最大和記錄并返回。
其時間復雜度為O(n^2)
,但是數據量大了后算法效率很低。
-
分治法
分治法:將一個問題拆分成多個相似的小問題,并對其分別求解,如果拆出的問題依然復雜,就通過遞歸調用再次將子問題拆分,直到拆出的方法可以以簡單方式求得解,最后合并多個小問題的解,就是原問題的結果。
比如下面這段代碼:
public static int maxSubSum(int[] seq, int left, int right) {
int maxLeftSum = 0;
int maxRightSum = 0;
int leftBorderSum = 0;// 左半邊包含最右一位數的和
int rightBorderSum = 0;// 右半邊包含最左一位數的和
int maxLeftBorderSum = 0;
int maxRightBorderSum = 0;
/* 只有一個元素 */
if (left == right) {
if (seq[left] > 0) {
return seq[0];
} else {
return 0;
}
}
int center = (left + right) / 2;
maxLeftSum = maxSubSum(seq, left, center);
maxRightSum = maxSubSum(seq, center + 1, right);
/* 左半邊最大和 */
for (int i = center; i >= left; i--) {
leftBorderSum += seq[i];
if (leftBorderSum > maxLeftBorderSum)
maxLeftBorderSum = leftBorderSum;
}
/* 右半邊最大和 */
for (int i = center + 1; i <= right; i++) {
rightBorderSum += seq[i];
if (rightBorderSum > maxRightBorderSum)
maxRightBorderSum = rightBorderSum;
}
/* 返回三個值中的最大值 */
return Math.max(maxLeftBorderSum + maxRightBorderSum, Math.max(maxLeftSum, maxRightSum));
}
這個算法比較長,但是效率高了很多,時間復雜度是O(NlogN)
。思路:
- 方法傳入序列數組,左右兩邊索引。
- 如果只有一個元素,大于零返回自己,否則返回零。
- 獲得中間索引,用于將當前的序列數組分隔為兩份,“分而治之”。
- 通過遞歸調用自身方法獲得分隔出的兩個子序列最大值。
- 兩個for循環是獲得兩個子序列包含從分割處開始向兩邊求和,得出子序列的最大和。
這樣可以通過將這兩個子序列相加,得到整個序列的最大子序列和。 - 將左序列最大子序列和、右序列最大子序列和、包含分割的子序列和(兩個for結果的和)比較,最大的為所求值。
-
貪心算法
貪心算法:在對問題求解時,總是做出在當前看來是最好的選擇。就是說,不從整體最優上加以考慮,他所做出的僅是在某種意義上的局部最優解。,再遞推到全局。貪心算法不是對所有問題都能得到整體最優解,但對范圍相當廣泛的許多問題他能產生整體最優解或者是整體最優解的近似解。
對于最大子序列問題,算法如下:
public static int maxSubSum(int[] seq) {
int maxSum = 0;
int thisSum = 0;
for (int i = 0; i < seq.length; i++) {
thisSum += seq[i];
if (thisSum > maxSum) {
maxSum = thisSum;
} else if (thisSum < 0) {
thisSum = 0;
}
}
return maxSum;
}
這個算法,難理解主要在else if (thisSum < 0) thisSum = 0;
這里。
我們知道,任何一個最大子序列和,它的第一位都不會是負數。結合貪心算法的定義,當thisSum
小于零時,即使加上后面的數也不會是最大子序列和(不如直接不要當前的thisSum
),所以直接將thisSum
設為0,重新開始計算新的子序列和。
這個算法的時間復雜度為O(N)
,相對于第一種窮舉法,效率高了很多。
<br /><br />