最大子序列和问题

最大子序列和问题: 给你一堆数据,比如  -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, 起始位置与结束位置可以存储在单链表中。

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值