求子数组之和的最大值是一个很经典的问题。问题的描述如下:一个有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;
}