解题笔记(1)——子数组之和的最大值

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

你可能感兴趣的:(解题笔记(1)——子数组之和的最大值)