数组的连续最大子段和

转至:博客园-Anker's Blog

问题描述:输入是一个大小为n的整型数组,要求输出数组的任何连续子数组中的最大值。例如:输入的数组为array[10] = {31,-41,59,26,-53,58,97,-93,-23,84};输出最大连续子数组和为array[2...6]:187

       算法1:对所有满足0<=i<=j<=n的(i,j)整数对进行迭代,对每个整数对,程序都要计算array[i...j]的总和,并检验该总和是否大于迄今为止的最大总和。

算法1的伪代码描述如下:

maxsofar = 0
for(i=0;i<n;++j)
  for(j=i;j<n;++j)
    tmepsum = 0
for(k=i;k<=j;++k)
      tempsum += array[k]
      maxsofar = max(maxsofar,tempmax)
这段代码简洁明了,便于理解,但是程序执行的速度很慢,时间复杂度为O(n^3)。
       算法2:对于算法1有一个明显的方法可以使其运行起来快得多。使得时间复杂度控制住平方O(n^2)。
第一个平方算法注意到,array[i...j]的总和与前面计算出的总和(array[i...j-1])密切相关,利用这一点可以达到算法2。
算法2_1的伪代码描述如下:

maxsofar = 0
 for(i=0;i<n;++i)
   tempsum = 0;
   for(j=i;j<n;++j)
 tempsum += array[j]
     maxsofar = max(maxsofar,tempsum)
第二个平方算法是引入一个数组curarray,大小也为n,通过空间来换取时间,通过访问外循环执行之前计算[0...i]各个连续字段总和。curarrary中的第i个元素包含array[0...i]中各个数的累加和,所以x[i...j]中各个数的总和可以通过计算curarray[j] -curarray[i-1]得到.
       算法2_2的伪代码描述如下:

curarray[-1] = 0
for(i=0;i<n;++i)
  curarray[i] = curarray[i-1]+x[i]
maxsofar = 0
for(i=0;i<n;++i)
   for(j=i;j<n;++j)
      sum = curarray[j]-curarray[i-1]
maxsofar = max(maxsofar,sum)
算法3:可以考虑采用法治算法。初始问题是要处理大小为n的数组,所以可以将其划分为两个子数组a和b,然后递归的找出a、b中元素总和最大的子数组分别为MaxA、MaxB。而最大子数组要么在a中,要么在b中,要么跨越a和b之间的边界,我们将跨越边界的最大子数组记为MaxC。我们通过分治算法计算处了MaxA和MaxB,通过某种办法计算处MaxC。然后返回三个中的最大值就是我们所要的最大子数组和。算法的时间复杂度为O(nlogn)。如何计算MaxC呢?通过观察发现,MaxC在a中的部分是a中包含右边界的最大子数组,而MaxC在b中的部分是b中包含左边界的最大子数组。将这些综合一起我们得到算法3:

int maxsum3(1,n)

{  if(n<1) //空数组

    return 0
  if(n==1)  
//只有一个元素的数组
    return array[1]
   mid = n/2  //分为两部分
  lmax = tempsum =0
  //包含右边界的最大子数组和
  for(i=mid;i>=1;--i)
    sum + array[i]
  lmax = max(lmax,sum)
  rmax = sum =0;//包含左边界的最大子数组和  
 for(i=mid;i<n;++i)
     sum += array[i] 
   rmax = max(rmax,sum)
  return max(lmax+rmax,maxsum3(1,mid),maxsum3(mid+1,n))
}
算法4:我们现在采用操作数组的最简单的算法:从数组最左端(元素x[0])开始扫描,一直到最右端(元素array[n-1])为止,并记下所遇到的最大总和的子数组。最大总和开始设为0.假设我们已经解决了array[0...i-1]的问题,那么如何将其扩展为包含x[i]的问题呢?我们用类似于分治算法的原理:前i个元素中,最大总和子数组要么在前i-1个元素中(将其存maxsofar中),要么其结束位置为i(将其存入maxendinghere中)。不从头开始计算结束位置为i的最大子数组,而是利用结束位置为i-1的最大子数组进行计算。这样就得到了算法4:

maxsofar = 0
maxendinghere = 0
for(i=0;i<n;++i)
  maxendinghere = max(maxendinghere+array[i],0)
maxsofar = max(maxsofar,maxendinghere)
理解这个程序的关键在于maxendinghere。在循环中第一个赋值语句之前,maxendinghere是结束位置为i-1的最大子数组的和,赋值语句将其修改为结束位置为i的最大子数组的和。若加上array[i]的后的结果为正值,则该赋值语句使maxendinghere增大x[i],若加上x[i]之后结果为负值,该赋值语句将maxendinghere重新设置为0(因为结束位置为i的最大子数组现在为空)。这个地方有些难度,需要认真思考揣摩。时间复杂度为O(n),线性算法,效率最高。

下面针对这4个算法写一个完成的程序来进行测试,程序如下:

。#include <iostream>
using namespace std; //求两个数种最大值
int max(const int m,const int n)
{
   return m>n ? m : n;
} //求三个整数中的最大值
int max(const int x,const int y,const int z)
{
  int temp = x>y ?  x : y;
  temp = temp > z ? temp : z;
  return temp;
} //算法1函数实现
int maxsum1(int *array,const size_t len)
{
  int maxsofar = 0;
  int tempsum = 0;
  for(size_t i=0;i<len;++i)
    for(size_t  j=i;j<len;++j)
    {     
      tempsum = 0;
      for(size_t k =i;k<=j;++k)
      {
         tempsum += array[k];
         maxsofar = max(maxsofar,tempsum);
      }
    }
  return maxsofar;
} //算法2.1的实现
int maxsum2_1(int *array,const size_t len)
{
  int maxsofar = 0;
  int tempsum = 0;
  for(size_t i=0;i<len;++i)
  {
      tempsum = 0;
      for(size_t  j=i;j<len;++j)
      {
         tempsum += array[j];
         maxsofar = max(maxsofar,tempsum);
      }
  }
  return maxsofar;
} //算法2.2的实现
int maxsum2_2(int *array,const size_t len)
{
   int *curarray =NULL;
   int maxsofar = 0;
   if(len>0)
     curarray = new int[len];
   curarray[-1] = 0;
   for(size_t  i=0;i<len;++i)
     curarray[i] = curarray[i-1] + array[i];
   for(size_t  j=0;j<len;++j)
     for(size_t  k=j;k<len;++k)
         //tempsum = curarray[k] - curarray[j-1];
        maxsofar = max(maxsofar,curarray[k]-curarray[j-1]);
   return maxsofar;
} //算法3的实现
int maxsum3(int *array,const int begin,const int end)
{
   int mid = 0;
   int lmax=0,rmax =0;
   int tempsum = 0;
   if(begin==end)
     return array[begin];
   mid = (begin+end) / 2;
   for(int i=mid;i>=begin;--i)
   {
        tempsum += array[i];
        lmax = max(lmax,tempsum);
   }
   tempsum = 0;
   for(int j=mid+1;j<=end;++j)
   {
     tempsum += array[j];
     rmax = max(rmax,tempsum);
   }
   return max(lmax+rmax,maxsum3(array,begin,mid),maxsum3(array,mid+1,end));
} //算法4的实现
int maxsum4(int *array,const size_t len)
{
   int maxendinghere = 0;
   int maxsofar = 0;
   for(size_t  i=0;i<len;++i)
   { 
      maxendinghere = max(maxendinghere+array[i],0);
      maxsofar = max(maxsofar,maxendinghere);
   }
   return maxsofar;
} int main()
{
   int array[10] = {31,-41,59,26,-53,58,97,-93,-23,84};
   int choise;
   cout<<"1.算法1"<<endl;
   cout<<"2.算法2_1"<<endl;
   cout<<"1.算法1"<<endl;
   cout<<"3.算法3"<<endl;
   cout<<"4.算法4"<<endl;  
   cout<<"5.算法2_2"  <<endl;
   cout<<"0.退出"<<endl;
   while(1)
   {
     cout<<"选择算法:"; 
     cin>>choise;
     cout<<"数组的最大字段和为:";
     switch(choise)
     {
     case 1:
       cout<<maxsum1(array,10)<<endl;
       break;
     case 2:
       cout<<maxsum2_1(array,10)<<endl;
       break;
     case 3:
       cout<<maxsum3(array,0,9)<<endl;
       break;
     case 4:
       cout<<maxsum4(array,10)<<endl;
       break;
     case 5:
       cout<<maxsum2_2(array,10)<<endl;
       break;
     case 0:
       exit(0);
     }
   }
   return 0;
}
参考文献:《编程珠玑》第二版 第八章 算法设计的艺术
冷静思考,勇敢面对,把握未来!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值