最大子序列和问题: 给你一堆数据,比如 -2,11,-4,13,-5,-2;叫你找出一连串的数字,要求这一连串的数字的和 是 所有一连串数字和中 的最大值。 要求给出这一连串数字的起始位置与结束位置 以及和。
算法1:
穷举法,两个变量 i 与j ,分别表示起始位置与结束位置,(所以肯定要求j>=i)。 穷举所有序列串的和;代码如下:复杂度为O(n^3)
int algorithm_1(const int num[], const int length, int* startPosition, int* finishPosition)
{
int i,j;
int MaxSum=0;
for(i=0; i<length; i++)
{
for(j=i; j<length;j++)
{
int k;
int sum =0;
for(k=i; k<=j; k++)
{
sum +=num[k];
}
if (sum>MaxSum)
{
MaxSum = sum;
*startPosition = i;
*finishPosition =j;
}
}
}
return MaxSum;
}
算法2:改进上面的算法,只用一个变量i标识起始位置,依然属于穷举法。 穷举 从每个位置出发,每个序列串的和。时间复杂度为O(n^2)
int algorithm_2(const int num[], const int length, int * startPosition, int * finishPosition)
{
int i;
int MaxSum =0;
for(i =0; i<length; i++)
{
int k;
int sum=0;
for(k=i; k<length; k++)
{
sum += num[k];
if(sum>MaxSum)
{
MaxSum = sum;
*startPosition =i;
*finishPosition =k;
}
}
}
return MaxSum;
}
算法3:采用分治算法,思路是将N个数分成两份,最大子序列和在左边或者右边,或者两者都有(这就要求包含左边的最右那个数与右边的最左那个数),
时间复杂度为O(NlogN)。时间复杂度要会算!
代码如下:(有点乱啊,呵呵)
int algorithm_3(const int num[], const int left, const int right, int * startPosition, int * finishPosition)
{
int center;
int MaxSum =0;
int MaxLeft, MaxRight,leftStart,leftFinish,rightStart,rightFinish; //用来标记左右两块的最大子序列
int begin,finish,sumLeft,sumRight,totalSum; //用来统计穿越中间的最大子序列
int k;
int sum;
/*递归结束*/
if(left == right)
{
if(num[left]>0)
return num[left];
else
return 0;
}
center =(left+right)/2;
MaxLeft=algorithm_3(num, left,center,&leftStart,&leftFinish);
MaxRight=algorithm_3(num,center+1,right,&rightStart,&rightFinish);
/*获得跨中间边界的最大和*/
sum=0;
sumLeft=0;
sumRight=0;
begin=finish=center;
for( k=center;k>=left;k--)
{
sum +=num[k];
if( sum>sumLeft)
{
sumLeft = sum;
begin = k;
}
}
sum =0;
for(k=center+1;k<=right;k++)
{
sum +=num[k];
if(sum>sumRight)
{
sumRight = sum;
finish =k;
}
}
totalSum =sumRight+sumLeft;
/*三者中取得最大值*/
{
int temp;
if(MaxLeft > MaxRight)
{
temp = MaxLeft;
*startPosition = leftStart ;
*finishPosition = leftFinish;
}
else
{
temp = MaxRight;
*startPosition = rightStart ;
*finishPosition = rightFinish;
}
if(temp>totalSum)
{
MaxSum = temp;
}
else
{
MaxSum = totalSum;
*startPosition = begin;
*finishPosition = finish;
}
}
return MaxSum;
}
算法4: 思路,比如一个序列 3, -2 , -1 ,-1, 4, -2, 9; 第一个数位3,所以可能会和后面的结合,组成一个更大的和;当然3要保存起来,作为临时sum
前两个和为1,也可能和后面组合,组成一个更大的和。 因为1<3,所以sum值不变
前三个数和为0;所以和后面组合的话,对整个和没有什么正面效果。
所以,下面若会出现更大的子序列,则至少从第4个数开始。
int algorithm_4(const int num[],const int length, int * startPosition, int * finishPosition)
{
int i;
int sum=0;
int MaxSum=0;
int flag=0;
for(i=0; i<length; i++)
{
sum +=num[i];
if(sum>MaxSum)
{
MaxSum=sum;
*finishPosition = i;
if(flag ==0) //起始位置
{
*startPosition =i;
flag =1;
}
}
else if(sum<=0)
{
sum=0;
flag =0;
}
}
return MaxSum;
}
需要注意的是,上面代码均没有考虑多个子序列有相同的最大和时,输出所有的子序列的下标。
那么这个问题该如何解决呢?我们可以用MaxSum<=sum代替MaxSum<sum, 起始位置与结束位置可以存储在单链表中。