一、题目:
返回一个整数数组中最大子数组的和。
要求:
输入一个整形数组,数组里有正数也有负数。
数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。
如 果数组A[0]……A[j-1]首尾相邻,允许A[i-1], …… A[n-1], A[0]……A[j-1]之和最大。
同时返回最大子数组的位置。
求所有子数组的和的最大值。要求时间复杂度为O(n)。
二、设计思路
开始想的是用链表实现数组首尾相连,可是由于链表用着不太熟悉,便想了另一种算法:
当最大值未出现跨域是就按常规方法计算找出,
出现跨域时,既然a[0] a[n-1]都出现在解
中
,则可以将其解延伸到最大,
也就是从a[0]开始,向右扫描,从a[n-1]开始向左扫描,
两者直到出现负数为止, 这样就不会出现中间是负数,但是两端是正数,但却选择整个
序列的情况。
三、源代码
//作者:赵建松、张文冬#include <iostream.h> #include <stdlib.h> /* * 现在是数组为一个环形结构,最大连续和又该如何实现? * 1.最简单的方法:数组循环移位,再进行逐一求解最大连续和,算法复杂度O(n^2) * 2.s[i][l] = s[i][l-1]+a[i+l-1]; sum = max{s[i][l]},s[i][l]:以i开始长度为l的连续和 * O(n) 的解法: * 1.解未出现跨域的情况,也就是a[0] ~ a[n-1](原问题) * 2.解出现跨域情况 a[n-1]~a[0] 出现在解的中间或边界 */ int max_subarray_bound(int a[],int from,int to) { int i,max,sum; max = a[from]; sum = a[from]; for(i = from + 1;i <= to;i++) { if(sum < 0) { sum = 0; } sum += a[i]; if(max < sum) { max = sum; } } return max; } int max_subarray_cycle(int a[],int size) { int sum,i,j,s,m; //第一种情况,未跨域 sum = max_subarray_bound(a,0,size-1); /* * 第二种情况,出现跨域,既然a[0] a[n-1]都出现在解中,则可以将其解延伸到最大, * 也就是从a[0]开始,向右扫描,从a[n-1]开始向左扫描,两者直到出现负数为止, * 这样就不会出现中间是负数,但是两端是正数,但却选择整个序列的情况. */ if(size == 2) return sum; i = 1; j = size-2; s = a[i-1] + a[j+1]; while(i < j && (a[i] >=0 || a[j] >= 0)) { if(a[i] >= 0) { s += a[i]; i++; } if(a[j] >= 0) { s += a[j]; j--; } } if(i == j) { if(a[i] >0) s+=a[i]; if(s < sum) return sum; else return s; } if(i < j) { //算出以i为头部的所有连续子数组和或者以j为尾的连续子数组和,从而找到最大的元素 int s2,k; m = a[i]; s2 = m; for(k=i+1;k <= j;k++) { s2 += a[k]; if(m < s2) m = s2; } s2 = a[j]; for(k = j-1;k>=i;k--) { s2 += a[j]; if(m < s2) m = s2; } if(m > 0) s += m; } if(s < sum) return sum; else return s; } int main(int argc,char *argv[]) { int a[] = {5,-3,-1,5,10,11}; for(int j=0;j<6;j++) { cout<<a[j]<<" "; } cout<<endl<<"最大子数组和为:"<<max_subarray_cycle(a,sizeof(a)/sizeof(a[0]))<<endl; return 0; }
四、截图
五、体会
在实现环形数组求最大子数组的过程中,我和张文冬分别想了不一样的方法来实现环形数组的问题,但是都存在缺陷和不容易实现的地方,最后找到了这种方法,但由于时间紧迫,功能实现的也不是特别完善,但在讨论各种方法的过程中也发散了我们的思维,有了收获。
时间: 2024-10-12 14:00:27