求子数组之和的最大值是一个很经典的问题。问题的描述如下:一个有N个整形数的一维数组(A[0], A[1], ... A[n-1]),这个数组有很多子数组,那么子数组之和的最大值是什么呢?
这个问题的解答其实在《编程珠玑》一书有的。一共是4中方法:第一种是穷举法,计算所有可能子数组的和,时间复杂度为O(n3)。第二种其实也是穷举法。代码如下:
for(i = 0;i < n;i++) { sum = 0; for(j = i;j < n;j++) { sum += A[j]; if(sum > maxsum) maxsum = sum; } }
很明显复杂度为O(n2)。第三种方法是分治法,将数组元素均分成两部分,那么最大子数组和只有三种情况。在左边部分,右边部分,以及跨越了边界部分。这种方法是时间复杂度为O(nlogn)。不是最优的就不列代码了。第四种是最优的,时间复杂度为O(n),利用了动态规划的思想。具体代码如下:
int MaxSubSum1(int *A,int n) { int start, all; all = start = A[n-1]; for(int i = n-2; i >= 0; i--) { start = Max(A[i], start + A[i]); all = Max(all, start); } return all; }
这种方法不论是空间和时间都已是最优的了,在《编程之美》中列举了改进的过程,最终的程序就是上面的这段代码。
如果是二维数组呢,又当如何解答。《编程之美》中给出的解法是穷举矩形区域的所有可能的上下边界,再用一维的方法计算该上下边界的最大和。时间复杂度为 O(n2*m)。当然也可以先穷举矩形区域的所有可能的左右边界。本质上是一样的。下面给出了一种解法。这里用了一个辅助数组B,B[ i ][ j ]表示第 j 列元素的前 i 行元素的和。B[ i + 1 ][ j ]=A[ 0 ][ j ]+A[ 1 ][ j ]+...A[ i ][ j ]。B[ 0 ][ j ]做为哨兵,全部为0。
int MaxSubSum2(int *A, int n, int m) { int i, j, k; //初始化辅助数组 int **B = new int*[n+1]; for(i = 0; i <= n; i++) B[i] = new int[m]; for(j = 0; j < m; j++) //第0行做为哨兵 B[0][j] = 0; for(i = 0; i < n; i++) for(j = 0; j < m; j++) B[i+1][j] = B[i][j]+A[i*n+j]; //开始计算 int maxsum = 0x80000000; //设为最小值 for(i = 1; i <= n; i++) { for(j = i; j <= n; j++) { int start, all; start = all = (B[j][m-1]-B[i-1][m-1]); for(k = m-2; k >= 0; k--) { start = Max(B[j][k]-B[i-1][k], start + B[j][k]-B[i-1][k]); all = Max(all, start); } if(all > maxsum) maxsum = all; } } for(i = 0; i <= n; i++) //释放辅助空间 delete [] B[i]; delete B; return maxsum; }
本人享有博客文章的版权,转载请标明出处 http://blog.csdn.net/wuzhekai1985