最大子序列和问题
给定一组序列,求子序列之和的最大值,其中这里的子序列要求是连续的,及求连续子数组最大和。
穷举法
最基本的想法就是列出所有的和然后取其最大即可。
public static int maxSubSum1(int[] a){
int maxSum = 0;
for(int i = 0; i < a.length; i++) {
int temp = 0;
for (int j = i; j < a.length; j++) {
temp += a[j];
if (temp > maxSum)
maxSum = temp;
}
}
return maxSum;
}
效率可见是很低的,算法复杂度为O(N^2)
Kadane算法/动态规划
- 在数组或滑动窗口中找到字串和的最大值或最小值的O(N)算法,它基于动态规划,是一个联机算法(联机算法是在任意时刻算法对要操作的数据只读入(扫描)一次,一旦被读入并处理,它就不需要在被记忆了。而在此处理过程中算法能对它已经读入的数据立即给出相应子序列问题的正确答案)。仅仅需要常量空间并以线性时间运行的联机算法几乎是完美的算法。
- 上述穷举法中,易知,j 代表当前序列的终点,而 i 代表当前序列的起点,如果我们不需要知道具体最佳的子序列在哪里,那么 i 的使用就可以从程序中被优化。
public static int maxSubSum2(int[] a){
if(a == null){
return 0;
}
int maxSum = a[0], thisSum = a[0];
for(int j=1; j<a.length; j++){
if (thisSum>0){
thisSum = thisSum + a[j];
}else {
thisSum = a[j];
}
maxSum = Math.max(maxSum,thisSum);
}
return maxSum;
}
该算法的时间复杂度为O(N),可以在线性时间内解决问题。
该算法的正确性不易看出,可以自行举例用以加深理解。
分治法
- 分:将问题分为两个大致相等的子问题,然后递归对他们求解;
- 治:将两个子问题的解修补到一起并可能再做些少量部队附加工作,最后得到整个问题的解。
最大连续子序列和要么出现在数组左半部分,要么出现在数组右半部分,要么横跨左右两半部分。因此求出这三种情况下的最大值就可以得到最大连续子序列和。
public static int maxSumRec(int[] a, int left, int right){
if(left == right)
if(a[left] > 0)
return a[left];
else
return 0;
int center = (left + right)/2;
int maxLeftSum = maxSumRec(a,left,center);
int maxRightSum = maxSumRec(a,center+1,right);
int maxLeftBorderSum = 0,leftBorderSum = 0;
for (int i = center; i >= left; i--){
leftBorderSum += a[i];
if(leftBorderSum > maxLeftBorderSum)
maxLeftBorderSum = leftBorderSum;
}
int maxRightBorderSum = 0,rightBorderSum = 0;
for(int i = center+1; i <= right; i++){
rightBorderSum += a[i];
if(rightBorderSum > maxRightBorderSum)
maxRightBorderSum = rightBorderSum;
}
return max3(maxLeftSum,maxRightSum,maxLeftBorderSum+maxRightBorderSum);
}
private static int max3(int a ,int b, int c) {
int temp = (a>b)?a:b;
int max = (temp>c)?temp:c;
return max;
}
public static int maxSubSum3(int[] a){
return maxSumRec(a,0,a.length-1);
}
该算法的时间复杂度为O(N*logN)
循环列表中的最大子序列和问题
序列首尾相连,可知问题的解分为两种情况:
- 1.最大序列没有跨越array[n-1] 到array[0];
- 2.最大子序列跨越了array[n-1] 到array[0]。
所以可以将两种情况都列出来再取其中的最大值即可。
首先可以考虑Kadane算法,我们可以一次移动长度为n的窗口,用Kadane求出当前窗口的最大子序列和,然后再将这些窗口分别的最大子序列和做比较,求得其中的最大者,而由于窗口数量最大为n,而Kadane算法是线性的,所以该解法的时间复杂度为O(N^2)。
下面考虑更简便的算法:
上述第一种情况已经顺利求解,对于第二种情况,可以想到
m
a
x
=
原
问
题
的
最
大
数
组
和
,
数
组
所
有
元
素
总
和
−
最
小
子
数
组
和
max={原问题的最大数组和,数组所有元素总和-最小子数组和}
max=原问题的最大数组和,数组所有元素总和−最小子数组和
而求解最小子数组和就是把所有元素取负数,再求最大子数组和即可。
代码与以上同理 。该方法复杂度为O(N)。
今日推歌
----《过活》高鱼
你爱自由胜过爱我
可是我偏偏
偏爱缠绵胜过洒脱
我们的契合
只是很美丽的花火
惊艳了时光
却照不亮挑剔的生活