题目
求整数数组的最大子序列和。子序列一定是连续的。
实现方式一
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)。
分析
两种实现方式从时间复杂度上来看第二种要优于第一中。而且第一种使用了递归,这使得它在计算时是需要将整个数组放在内存里的,而第二种其实是不需要的,它只需要逐个读取数组的元素即可,这样来看,第二种算法在内存空间消耗上也比第一种好。因此,第二种算法是最优的。