给定一整数序列A1, A2,... An (可能有负数),求A1~An的一个子序列Ai~Aj,使得Ai到Aj的和最大
例如:整数序列-2, 11, -4, 13, -5, 2, -5, -3, 12, -9的最大子序列的和为21。对于这个问题,最简单也是最容易想到的那就是穷举所有子序列的方法。利用三重循环,依次求出所有子序列的和然后取最大的那个。当然算法复杂度会达到O(n^3)。
算法一:
long maxSubSum1( const vector<int> & a )
{
long maxSum = 0;
for (int i = 0; i < a.size(); i++) //i表示子序列起始下标
{
for (int j = i; j < a.size(); j++) //j表示子序列结束下标
{
long thisSum = 0;
for (int k = i; k <= j; k++) //遍历子序列的开头和结束下标,计算子序列的和
{
thisSum += a[k];
}
if (thisSum > maxSum) //判断最大子序列
maxSum = thisSum;
}
}
return maxSum;
}
这个算法很简单,i表示子序列起始下标,j表示子序列结束下标,遍历子序列的开头和结束下标,计算子序列的和,然后判断最大子序列。很明显的看出算法复杂度是O( pow( n, 3 ) )
算法本身很容易理解,而且很直观的感觉在最内层for循环中做了很多无用操作,例如:i = 0, j = 3时,会计算a[0] + a[1] +…+ a[3];而当i = 0, j = 4时候又会计算a[0] + a[1] +…a[4]。
算法二:
long maxSubSum2( const vector<int> & a )
{
long maxSum = 0;
for (int i = 0; i < a.size(); i++) //i表示子序列起始下标
{
long thisSum = 0;
for (int j = i; j < a.size(); j++) //j表示子序列结束下标
{
thisSum += a[j];
if (thisSum > maxSum) //thisSum表示a[i] + a[i+1] + … + a[j-1],相比算法1减少最内层的for循环
maxSum = thisSum;
}
}
return maxSum;
}
这是一个非常直观的穷举法(比上面的分析还有简单些),而且没有多余重复的操作,复杂度为
O(N^2)
。其中,
thisSum
表示
a[i] + a[i+1] + … + a[j-1]
。
算法三:
分治法:最大子序列和可能出现在三个地方:整个出现在输入数据的左半部分,整个出现在输入数据的右半部分,或者跨越输入数据的中部从而占据左右两个半部分。
代码如下:
long maxSubSum3( const vector<int> & a )//启动函数
{
return maxSubSum3(a,0,a.size()-1);
}
long maxSubSum3( const vector<int> & a,int left,int right )
{
if(left == right)//只有1个元素了
{
if(a[left] > 0)//他就是最大子列
{
return a[left];
}
else
{
return 0;
}
}
else
{
//开始分
int middle = (left + right)/2;
long maxLeftSum = maxSubSum3(a,left,middle);
long maxRightSum = maxSubSum3(a,middle+1,right);
//开始归并了
//求出前半部分(包含最后1个元素)最大值
long maxLeftBorderSum = 0, leftBorderSum = 0;
for(int i = middle; i >= left;i--)
{
leftBorderSum += a[i];
if(leftBorderSum > maxLeftBorderSum)
{
maxLeftBorderSum = leftBorderSum;
}
}
//求出后半部分(包含第1个元素)最大值
long maxRightBorderSum = 0, rightBorderSum = 0;
for(int i = middle+1; i <= right;i++)
{
rightBorderSum += a[i];
if(rightBorderSum > maxRightBorderSum)
{
maxRightBorderSum = rightBorderSum;
}
}
return max(maxLeftSum,maxRightSum,maxLeftBorderSum+maxRightBorderSum);
}
}
int max(int a,int b,int c)
{
int max = a;
if(a < b)
{
max = b;
}
if(b < c)
{
max = c;
}
return max;
}