例如输入的数组为1, -2, 3, 10, -4, 7, 2, -5,和最大的子数组为3, 10, -4, 7, 2,因此输出为该子数组的和18。
分析:本题最初为2005年浙江大学计算机系的考研题的最后一道程序设计题,在2006年里包括google在内的很多知名公司都把本题当作面试题。由于本题在网络中广为流传,本题也顺利成为2006年程序员面试题中经典中的经典。
如果不考虑时间复杂度,我们可以枚举出所有子数组并求出他们的和。不过非常遗憾的是,由于长度为n的数组有O(n2)个子数组;而且求一个长度为n的数组的和的时间复杂度为O(n)。因此这种思路的时间是O(n3)。
很容易理解,当我们加上一个正数时,和会增加;当我们加上一个负数时,和会减少。如果当前得到的和是个负数,那么这个和在接下来的累加中应该抛弃并重新清零,不然的话这个负数将会减少接下来的和。基于这样的思路,我们可以写出如下代码:
/* // Find the greatest sum of all sub-arrays // Return value: if the input is valid, return true, otherwise return false int *pData, // an array unsigned int nLength, // the length of array int &nGreatestSum // the greatest sum of all sub-arrays */ int start,end; bool FindGreatestSumOfSubArray(int *pData, unsigned int nLength, int &nGreatestSum) { // if the input is invalid, return false if((pData == NULL) || (nLength == 0)) return false; int k=0; int nCurSum = nGreatestSum = 0; for(unsigned int i = 0; i < nLength; ++i) { nCurSum += pData[i]; // if the current sum is negative, discard it if(nCurSum < 0) { nCurSum = 0; k = i+1; } // if a greater sum is found, update the greatest sum if(nCurSum > nGreatestSum) { nGreatestSum = nCurSum; start = k; end = i; } } // if all data are negative, find the greatest element in the array if(nGreatestSum == 0) { nGreatestSum = pData[0]; for(unsigned int i = 1; i < nLength; ++i) { if(pData[i] > nGreatestSum) { nGreatestSum = pData[i]; start = end = i; } } } return true; }讨论:上述代码中有两点值得和大家讨论一下:
- 函数的返回值不是子数组和的最大值,而是一个判断输入是否有效的标志。如果函数返回值的是子数组和的最大值,那么当输入一个空指针是应该返回什么呢?返回0?那这个函数的用户怎么区分输入无效和子数组和的最大值刚好是0这两中情况呢?基于这个考虑,本人认为把子数组和的最大值以引用的方式放到参数列表中,同时让函数返回一个函数是否正常执行的标志。
- 输入有一类特殊情况需要特殊处理。当输入数组中所有整数都是负数时,子数组和的最大值就是数组中的最大元素。
方法二:编程之美2.14
/** 求最大子数组和(编程之美2.14,返回下标及首尾不相连) ** author :liuzhiwei ** data :2011-08-17 **/ int maxSum(int *arr, int len, int & left, int & right) { int start = arr[0]; int sum = arr[0]; left=0; right=0; int cur_left=0; for(int i=1;i<len;i++) { if(start < 0) { start = arr[i]; //抛弃当前子序列 cur_left=i; //开始新的子序列搜索 } else { start+=arr[i]; } if(start > sum) //更新最大子序列 { sum=start; left=cur_left; right=i; //最大和增加,此时的i一定是最右端 } } return sum; } //特殊测试用例 -10 -1 -4最大子矩阵和
#include <iostream> #include <cstdio> using namespace std; #include <memory.h> int a[102][102]; int maxSubArray(int *arr, int len) //最大子序列和 { int i,sum=arr[0],b=0; for(i=0;i<len;++i) { if(b>0) b+=arr[i]; else b=arr[i]; if(b>sum) sum=b; } return sum; } int maxSubMatrix(int n, int m,int array[102][102]) { int i,j,h,max,sum=-100000; int b[102]; for(i=0;i<n;i++) { memset(b,0,sizeof(b)); //初始化b[] for(j=i;j<n;j++) //把第i行到第j行相加,对每一次相加求出最大值 { for(h=0;h<m;h++) { b[h]+=array[j][h]; //二维数组压缩成一维数组,然后求最大子序列和 } max=maxSubArray(b,h); if(max>sum) sum=max; } } return sum; } int main(void) { int n,i,j; while(scanf("%d",&n)!=EOF) { for(i=0;i<n;i++) { for(j=0;j<n;j++) scanf("%d",&a[i][j]); } printf("%d\n",maxSubMatrix(n,n,a)); } return 0; }转载自htp://zhedahht.blog.163.com/