題目
求整數數組的最大子序列和。子序列一定是連續的。
實現方式一
public long fun1(Integer[] arr, int left, int right) {
if(left == right) {
return arr[left];
}
int center = (left + right)/2;
long maxLeftSum = fun1(arr, left, center);
long maxRightSum = fun1(arr, center+1, right);
long maxLeftBorderSum = arr[center];
int leftBorderSum = 0;
for(int i=center;i>=left;i--) {
leftBorderSum += arr[i];
if(leftBorderSum > maxLeftBorderSum) {
maxLeftBorderSum = leftBorderSum;
}
}
long maxRightBorderSum = arr[center+1];
int rightBorderSum = 0;
for(int i=center+1;i<=right;i++) {
rightBorderSum += arr[i];
if(rightBorderSum > maxRightBorderSum) {
maxRightBorderSum = rightBorderSum;
}
}
long borderSum = maxLeftBorderSum + maxRightBorderSum;
long max = maxLeftSum;
if(maxRightSum > max) {
max = maxRightSum;
}
if(borderSum > max) {
max = borderSum;
}
return max;
}
這裏採用了分治算法,算法的思想是分三種情況:
- 最大子序列和出現在左半邊的子序列裏
- 最大子序列和出現在右版本的子序列裏
- 最大子序列和包含了左右兩邊的子序列
這個算法的時間複雜度相當於O(NlogN)。
實現方式二
public long fun2(Integer[] arr) {
long max = arr[0];
long currSum = 0;
for(int i=0;i<arr.length;i++) {
currSum += arr[i];
if(currSum > max) {
max = currSum;
}else if(currSum < 0) {
currSum = 0;
}
}
return max;
}
這種方式比較巧妙,它只循環一次,沒有遞歸,初始最大值就是數組第一個元素,然後開始遍歷數組,每遍歷一次就累加一次,並與當前最大值進行比較以更新最大值,當出現累加值小於0,則重新置0。
這種算法的時間複雜度是O(N)。
分析
兩種實現方式從時間複雜度上來看第二種要優於第一中。而且第一種使用了遞歸,這使得它在計算時是需要將整個數組放在內存裏的,而第二種其實是不需要的,它只需要逐個讀取數組的元素即可,這樣來看,第二種算法在內存空間消耗上也比第一種好。因此,第二種算法是最優的。