题目:
给定N个整数序列{A1,A2,A3,...,An},求出子序列(例:Ai-Aj,必须是连续的)中的最大值
下面给出此题的四种解决办法,且时间复杂度依次增加。
public int maxSubsequenceSum4(int[] list, int n){
int thisSum = 0;//当前子序列和
int maxSum = 0;//目前最大子序列和
for (int i = 0; i < n; i++){
thisSum += list[i];
if (thisSum > maxSum){
//当发现更大的子序列和时,替换此前的和
maxSum = thisSum;
}
/*当当前子序列和加上一个绝对值大于它的负数时,此子序列变成负数,
*此时应当抛弃该子序列,重新以下一个整数为开头寻找下一个子序列.
*例如:1,3,-5,4,2,-1,...
*显然子序列A1,A2,A3的和为负数,此时应当抛弃A1,A2,A3...(注意:此时maxSum为A1,A2的和)
*并以A4...开始寻找下一个合适的子序列
*
*/
else if (thisSum < 0){
thisSum = 0;
}
}
return maxSum;
}
int Max3( int A, int B, int C )
{
/* 返回3个整数中的最大值 */
return A > B ? A > C ? A : C : B > C ? B : C;
}
int DivideAndConquer(int[] list, int left, int right )
{
/* 分治法求list[left]到list[right]的最大子列和 */
int MaxLeftSum, MaxRightSum; /* 存放左右子问题的解 */
int MaxLeftBorderSum, MaxRightBorderSum; /*存放跨分界线的结果*/
int LeftBorderSum, RightBorderSum;
int center, i;
if( left == right ) {
/* 递归的终止条件,子列只有1个数字 */
if( list[left] > 0 ) return list[left];
else return 0;
}
/* 下面是"分"的过程 */
center = ( left + right ) / 2; /* 找到中分点 */
/* 递归求得两边子列的最大和 */
MaxLeftSum = DivideAndConquer( list, left, center );
MaxRightSum = DivideAndConquer( list, center+1, right );
/* 下面求跨分界线的最大子列和 */
MaxLeftBorderSum = 0; LeftBorderSum = 0;
for( i=center; i>=left; i-- ) {
/* 从中线向左扫描 */
LeftBorderSum += list[i];//子序列为list[center],list[center-1],...
if( LeftBorderSum > MaxLeftBorderSum )
MaxLeftBorderSum = LeftBorderSum;
} /* 左边扫描结束 */
MaxRightBorderSum = 0; RightBorderSum = 0;
for( i=center+1; i<=right; i++ ) {
/* 从中线向右扫描 */
RightBorderSum += list[i];//子序列为list[center+1],list[center+2],...
if( RightBorderSum > MaxRightBorderSum )
MaxRightBorderSum = RightBorderSum;
} /* 右边扫描结束 */
/* 下面返回"治"的结果 */
return Max3( MaxLeftSum, MaxRightSum, MaxLeftBorderSum + MaxRightBorderSum );
}
int maxSubsequenceSum3(int[] list, int N )
{
/* 保持与前2种算法相同的函数接口 */
return DivideAndConquer( list, 0, N-1 );
}
同样举例说明方法二
4 | -3 | 5 | -2 | -1 | 2 | 6 | -2 |
---|
以上为八个整数序列,下面用方法二求出八个整数序列的最大子序列
函数执行顺序图如下:
if( left == right ) {
/* 递归的终止条件,子列只有1个数字 */
if( list[left] > 0 ) return list[left];
else return 0;
}
然后再返回上一层调用函数,求出左右两部分的子序列最大值。注意:分割线两侧数据为起始数据
该方法主要就是以分割线两侧数据
为起始数据,两边分散(不中断)求左右子序列的和
最后跨分割线子序列最大值
为左侧最大值
+右侧最大值
(例如:(A1,A2,A3,A4),左侧:A1,A2右侧:A3,A4.假设其左侧最大子序列和为A2,A1,右侧大子序列和为A3,则跨分割线子序列最大值为A1,A2,A3)
/* 下面求跨分界线的最大子列和 */
MaxLeftBorderSum = 0; LeftBorderSum = 0;
for( i=center; i>=left; i-- ) {
/* 从中线向左扫描 */
LeftBorderSum += list[i];//子序列为list[center],list[center-1],...
if( LeftBorderSum > MaxLeftBorderSum )
MaxLeftBorderSum = LeftBorderSum;
} /* 左边扫描结束 */
MaxRightBorderSum = 0; RightBorderSum = 0;
for( i=center+1; i<=right; i++ ) {
/* 从中线向右扫描 */
RightBorderSum += list[i];//子序列为list[center+1],list[center+2],...
if( RightBorderSum > MaxRightBorderSum )
MaxRightBorderSum = RightBorderSum;
} /* 右边扫描结束 */
最后返回三个数据的最大值,即最大子序列和
/* 下面返回"治"的结果 */
return Max3( MaxLeftSum, MaxRightSum, MaxLeftBorderSum + MaxRightBorderSum );
public int maxSubsequenceSum2(int[] list, int n){
int thisSum = 0;
int maxSum = 0;
for (int i = 0; i < n; i++){
//循环n次,每次循环表示以Ai为子序列的起始数据
thisSum = 0;
for (int j = i; j < n; j++){
thisSum += list[j];//子序列为Ai,Ai+1,...
if (thisSum > maxSum){
maxSum = thisSum;
}
}
}
return maxSum;
}
时间复杂度T(n)=n3
public int maxSubsequenceSum1(int A[],int n){
int thisSum = 0;
int maxSum = 0;
for (int i = 0; i < n; i++){
for (int j = i; j < n; j++){
thisSum = 0;
for (int k = i; k <= j; k++ ){
thisSum += A[k];
if (thisSum > maxSum){
maxSum = thisSum;
}
}
}
}
return maxSum;
}
代码来自MOOC网浙江大学数据结构课程,本人稍作修改整理以及加上自己的解释