数组的连续最大子段和

  问题描述:输入是一个大小为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的伪代码描述如下:

1 maxsofar = 0
2 for(i=0;i<n;++j)
3   for(j=i;j<n;++j)
4     tmepsum = 0
5 for(k=i;k<=j;++k)
6       tempsum += array[k]
7       maxsofar = max(maxsofar,tempmax)

这段代码简洁明了,便于理解,但是程序执行的速度很慢,时间复杂度为O(n^3)。

  算法2:对于算法1有一个明显的方法可以使其运行起来快得多。使得时间复杂度控制住平方O(n^2)。

第一个平方算法注意到,array[i...j]的总和与前面计算出的总和(array[i...j-1])密切相关,利用这一点可以达到算法2。

算法2_1的伪代码描述如下:

1 maxsofar = 0
2 for(i=0;i<n;++i)
3   tempsum = 0;
4   for(j=i;j<n;++j)
5 tempsum += array[j]
6     maxsofar = max(maxsofar,tempsum)

第二个平方算法是引入一个数组curarray,大小也为n,通过空间来换取时间,通过访问外循环执行之前计算[0...i]各个连续字段总和。curarrary中的第i个元素包含array[0...i]中各个数的累加和,所以x[i...j]中各个数的总和可以通过计算curarray[j] -curarray[i-1]得到.

算法2_2的伪代码描述如下:

1 curarray[-1] = 0
2 for(i=0;i<n;++i)
3   curarray[i] = curarray[i-1]+x[i]
4 maxsofar = 0
5 for(i=0;i<n;++i)
6    for(j=i;j<n;++j)
7       sum = curarray[j]-curarray[i-1]
8 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:

 1 int maxsum3(1,n)
 2 {
 3   if(n<1)  //空数组
 4     return 0
 5   if(n==1)
 6 //只有一个元素的数组
 7     return array[1]
 8    mid = n/2  //分为两部分
 9   lmax = tempsum =0
10   //包含右边界的最大子数组和
11   for(i=mid;i>=1;--i)
12     sum + array[i]
13   lmax = max(lmax,sum)
14   rmax = sum =0;//包含左边界的最大子数组和
15  for(i=mid;i<n;++i)
16      sum += array[i]
17    rmax = max(rmax,sum)
18   return max(lmax+rmax,maxsum3(1,mid),maxsum3(mid+1,n))
19 }

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

1 maxsofar = 0
2 maxendinghere = 0
3 for(i=0;i<n;++i)
4   maxendinghere = max(maxendinghere+array[i],0)
5 maxsofar = max(maxsofar,maxendinghere)

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

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

  1 。#include <iostream>
  2 using namespace std; //求两个数种最大值
  3 int max(const int m,const int n)
  4 {
  5    return m>n ? m : n;
  6 } //求三个整数中的最大值
  7 int max(const int x,const int y,const int z)
  8 {
  9   int temp = x>y ?  x : y;
 10   temp = temp > z ? temp : z;
 11   return temp;
 12 } //算法1函数实现
 13 int maxsum1(int *array,const size_t len)
 14 {
 15   int maxsofar = 0;
 16   int tempsum = 0;
 17   for(size_t i=0;i<len;++i)
 18     for(size_t  j=i;j<len;++j)
 19     {
 20       tempsum = 0;
 21       for(size_t k =i;k<=j;++k)
 22       {
 23          tempsum += array[k];
 24          maxsofar = max(maxsofar,tempsum);
 25       }
 26     }
 27   return maxsofar;
 28 } //算法2.1的实现
 29 int maxsum2_1(int *array,const size_t len)
 30 {
 31   int maxsofar = 0;
 32   int tempsum = 0;
 33   for(size_t i=0;i<len;++i)
 34   {
 35       tempsum = 0;
 36       for(size_t  j=i;j<len;++j)
 37       {
 38          tempsum += array[j];
 39          maxsofar = max(maxsofar,tempsum);
 40       }
 41   }
 42   return maxsofar;
 43 } //算法2.2的实现
 44 int maxsum2_2(int *array,const size_t len)
 45 {
 46    int *curarray =NULL;
 47    int maxsofar = 0;
 48    if(len>0)
 49      curarray = new int[len];
 50    curarray[-1] = 0;
 51    for(size_t  i=0;i<len;++i)
 52      curarray[i] = curarray[i-1] + array[i];
 53    for(size_t  j=0;j<len;++j)
 54      for(size_t  k=j;k<len;++k)
 55          //tempsum = curarray[k] - curarray[j-1];
 56         maxsofar = max(maxsofar,curarray[k]-curarray[j-1]);
 57    return maxsofar;
 58 } //算法3的实现
 59 int maxsum3(int *array,const int begin,const int end)
 60 {
 61    int mid = 0;
 62    int lmax=0,rmax =0;
 63    int tempsum = 0;
 64    if(begin==end)
 65      return array[begin];
 66    mid = (begin+end) / 2;
 67    for(int i=mid;i>=begin;--i)
 68    {
 69         tempsum += array[i];
 70         lmax = max(lmax,tempsum);
 71    }
 72    tempsum = 0;
 73    for(int j=mid+1;j<=end;++j)
 74    {
 75      tempsum += array[j];
 76      rmax = max(rmax,tempsum);
 77    }
 78    return max(lmax+rmax,maxsum3(array,begin,mid),maxsum3(array,mid+1,end));
 79 } //算法4的实现
 80 int maxsum4(int *array,const size_t len)
 81 {
 82    int maxendinghere = 0;
 83    int maxsofar = 0;
 84    for(size_t  i=0;i<len;++i)
 85    {
 86       maxendinghere = max(maxendinghere+array[i],0);
 87       maxsofar = max(maxsofar,maxendinghere);
 88    }
 89    return maxsofar;
 90 } int main()
 91 {
 92    int array[10] = {31,-41,59,26,-53,58,97,-93,-23,84};
 93    int choise;
 94    cout<<"1.算法1"<<endl;
 95    cout<<"2.算法2_1"<<endl;
 96    cout<<"1.算法1"<<endl;
 97    cout<<"3.算法3"<<endl;
 98    cout<<"4.算法4"<<endl;
 99    cout<<"5.算法2_2"  <<endl;
100    cout<<"0.退出"<<endl;
101    while(1)
102    {
103      cout<<"选择算法:";
104      cin>>choise;
105      cout<<"数组的最大字段和为:";
106      switch(choise)
107      {
108      case 1:
109        cout<<maxsum1(array,10)<<endl;
110        break;
111      case 2:
112        cout<<maxsum2_1(array,10)<<endl;
113        break;
114      case 3:
115        cout<<maxsum3(array,0,9)<<endl;
116        break;
117      case 4:
118        cout<<maxsum4(array,10)<<endl;
119        break;
120      case 5:
121        cout<<maxsum2_2(array,10)<<endl;
122        break;
123      case 0:
124        exit(0);
125      }
126    }
127    return 0;
128 }

参考文献:《编程珠玑》第二版 第八章 算法设计的艺术

时间: 2024-11-03 04:51:39

数组的连续最大子段和的相关文章

java数组-这是我的代码,如何才能让数组实现连续的输入和输出

问题描述 这是我的代码,如何才能让数组实现连续的输入和输出 public static void main(String[] args) { // TODO Auto-generated method stub Scanner sc = new Scanner(System.in); System.out.println(""请输入数字个数""); int n = sc.nextInt(); System.out.println(""请输入数字&

Java求数组中连续n个元素使其和最大

给定一个数组,求出数组中连续的一些元素使其和的值最大.如果所有元素都为正数,显然整个数组即为所求的.如果所有元素的值为负数,则所求的最大值为0. 这是在编程珠玑上看到的,其时间复杂度由O(n3)减为O(n)了. java代码 package cn.lifx.test; public class MaxSum { public static void main(String[] args) { int[] arr = new int[]{31, -41, 59, 26, -53, 58, 97,

php求正负数数组中连续元素最大值示例

 问题是给出数组,该数组由正负数字组成,找出该数组中连续元素组成的子数组的最大值.下面是PHP实现的示例,需要的朋友可以参考下 php实现正负数数组最大子序列,要求给出数组,该数组由正负数字组成,找出该数组中连续元素组成的子数组的最大值. 这其实得算是个背包变种吧.   代码如下: <?php $list = array(1,-3,-5,-7,8,9,-11,5);   $cur = 0; $term = 0; $res = 0; $begin = 0;   foreach($list as $

php求正负数数组中连续元素最大值示例_php实例

php实现正负数数组最大子序列,要求给出数组,该数组由正负数字组成,找出该数组中连续元素组成的子数组的最大值.这其实得算是个背包变种吧. 复制代码 代码如下: <?php$list = array(1,-3,-5,-7,8,9,-11,5); $cur = 0;$term = 0;$res = 0;$begin = 0; foreach($list as $k => $v){ $cur += $v; if($cur < 0){  $cur = 0;  $begin = $k + 1; }

lintcode循环数组之连续子数组求和

v 题目:连续子数组求和 II 给定一个整数循环数组(头尾相接),请找出一个连续的子数组,使得该子数组的和最大.输出答案时,请分别返回第一个数字和最后一个数字的值.如果多个答案,请返回其中任意一个. v 样例  给定 [3, 1, -100, -3, 4], 返回 [4,0]. v 思路 1.如果不是循环数组,求解连续子区间和的思路如下: 首先设一个累加变量和sum和最大值变量maxN,[ld, rd]表示当前正在累加的区间,[lt,rt]表示最大和的区间.从左边开始一直累加,并初始当前区间[l

c语言 数组-请教如何用c语言去除一个数组中所有值为零的元素,而且这些零元素中有连续排列的?

问题描述 请教如何用c语言去除一个数组中所有值为零的元素,而且这些零元素中有连续排列的? 能否给一个示例程序?感激不尽! 比如以下这个数组中有连续的0元素,如何去除所有的零元素? double a[64]={4.63866e+020,1.456e+027,-7.67487e+017,9.86481e+016,0,0,-3.1101e+014,-9.38282e+010, 1.456e+027,4.60249e+033,-2.3969e+024,3.36857e+023,0,0,-9.64264e

JavaScript实现找出数组中最长的连续数字序列_javascript技巧

原始题目: 给定一个无序的整数序列, 找最长的连续数字序列. 例如: 给定[100, 4, 200, 1, 3, 2], 最长的连续数字序列是[1, 2, 3, 4]. 小菜给出的解法: function maxSequence(array,step){ var _array = array.slice(), //clone array _step = 1, _arrayTemp = [], i = 0; var parseLogic = { //result container parseRe

数组大小为2n+1-数组相关算法java,找出需求的数据

问题描述 数组相关算法java,找出需求的数据 存在一个数组,数组大小为2n+2,里面有n对个数,例如:1,2,2,3,4,1.(数组是无序的,考虑排序的话一定会超过限制)这,6个数中的单独的数就是3,4,要你用你能想到的最高效率的方法找出来 解决方案 如果数组是连续的则可以用byte[] b = new byte[n+1];然后遍历一遍原数组,将遍历的值放入b的下标中计数,最后为1的那个下标表示数据是单独的. 这样的话总最多做3n+3次操作就能找全单独的数. 如果数组里面的数是无规律的,那么可

二维数组与指针-C语言二维数组中的*(p+1)的确切含义

问题描述 C语言二维数组中的*(p+1)的确切含义 各位大师们,烦请指教一二吧.如果是在一维数组中,*(p+1)表示p+1这个地址空间或空间中的值,那么在二维数组中,p+1是指向a[1]*(p+1)是a1这个地址中的值啊?可是为什么会是地址呢? 解决方案 二维数组其实是一个小戏法,本质上还是一维数组--二维下标连续构成的数组又连续构成第一维下标.你可以像遍历一维数组那样遍历它 解决方案二: 其实a[2][3]的调用可以看成是两个调用,首先是对a进行[2]操作,然后再对a[2]的返回值进行[3]操