[编程之美2.14]求子数组之和的最大值

探讨了一维数组中寻找最大子数组和的四种不同算法思路及其实现:暴力解法(O(N^2))、递归分治(O(nlogn))、动态规划简化版(O(n))及高效动态规划(O(n))。并提供了每种方法的具体代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题描述:一个有N个整数元素的一维数组(A[0]...A[n-1]),这个数组当然有很多子数组,那么子数组之和的最大值是多少呢?

思路一:最直接能想到的方法就是枚举,暴力解法,复杂度为O(N^2)。

思路二:递归解法,将数组分成长度相等的两段数组,分别为A[0],...,A[n/2-1]和A[n/2],...,A[n-1],分别求出最大字段和,原数组的最大字段和为以下三种情况之一:

1:A[0],..., A[n-1]的最大字段和A[0],...,A[n/2-1]的最大字段相同

2:A[0],..., A[n-1]的最大字段和A[n/2],...,A[n-1]]的最大字段相同

3:A[0],..., A[n-1]的最大字段跨过了中间两个元素A[n/2-1]到A[n/2]。
这其实是一种分治算法,复杂度为O(nlogn)
思路三:动态规划思想,从A[n-1]算起到A[i]时候,最大的和在{A[i],nAll,nStart}(nAll表示A[n-1]到A[i+1]的子数组最大值,nStart表示含A[i]的子数组的最大值)中,故可以用动态规划的 方法解决。
思路四:这种方法从动态规划的思想想到的,很巧妙,从数组一直累加求最大值,如果和大于0,则可以一直累加,如果和小于0了,就直接舍弃掉前面重新再求和。

详见代码如下:
#include <algorithm>
#include <iostream>
#include <stdlib.h>

using namespace std;


int maxSum_1(int *A, int n)		// O(n^2),暴力解法
{
	int maximum = -100000000;
	int sum;
	for(int i = 0; i < n; i++)
	{
		sum = 0;
		for(int j = i; j < n; j++)
		{
			sum += A[j];
			if(sum > maximum)
				maximum = sum;
		}
	}
	return maximum;

}

int maxSum_2(int * A, int low, int high)	// O(nlogn),递归
{
	if(low == high)
		return A[low];

	int mid = low + ((high - low) >> 1);
	int maxLeftSum = maxSum_2(A, low, mid);
	int maxRightSum = maxSum_2(A, mid + 1, high);

	// find the maximum sum of a sequnce crossing the midpoint
	int maxLS, curLS;	
	maxLS = curLS = A[mid];
	/* low <= mid */
	for (int i = mid - 1; i >= low; --i)
	{
		curLS += A[i];
		if (curLS > maxLS)
		{
			maxLS = curLS;
		}
	}

	int maxRS, curRS;
	maxRS = curRS = A[mid+1];
	/* mid+1 <= high */
	for (int j = mid + 2; j <= high; ++j)
	{
		curRS += A[j];
		if (curRS > maxRS)
		{
			maxRS = curRS;
		}	
	}

	int maxMidSum = maxLS + maxRS;
	return max(max(maxLeftSum, maxRightSum), maxMidSum); 
}



int maxSum_3(int *A, int n)	// O(n),最简单
{
	int nStart = A[n-1];
	int nAll = A[n-1];
	for(int i = n - 2; i >= 0; i--)
	{
		nStart = max(A[i], nStart + A[i]);
		nAll = max(nStart, nAll);
	}
	return nAll;
}

//O(n),从A[n-1]算起到A[i]时候,最大的和在{A[i],nAll,nStart}(nAll表示A[n-1]到A[i+1]的子数组最大值,nStart表示含A[i]的子数组的最大值)中,故可以用动态规划的方法解决。
int maxSum_4(int * A, int n)
{
	int sum = 0;
	int max = A[0];
	for(int i = 0;i < n; i++)
	{
		if(sum > 0)
			sum += A[i];
		else
			sum = A[i];
		if(sum > max)
			max = sum;
	}
	return max;
}
int main()
{
	int A[] = {-2,5,3,-6,4,-8,6};
	cout << maxSum_1(A, sizeof(A)/sizeof(A[0])) << endl;
	cout << maxSum_2(A, 0,sizeof(A)/sizeof(A[0])) << endl;
	cout << maxSum_3(A, sizeof(A)/sizeof(A[0]))<< endl;
	cout << maxSum_4(A, sizeof(A)/sizeof(A[0]))<< endl;
	system("pause");
}

扩展思考题:
1.如果数组首尾相连,请问使其和最大,怎么办?
2.如果题目要求同时返回最大数组的位置,算法应该如何改变,还能保持O(n)的时间复杂度么?


解:对于第一题,明天再写了,据说编程之美上的解答好像是有问题的,先附上链接http://www.ahathinking.com/archives/120.html#comments
第二题的话,运用方法4,当max变化时候,可以记录前后下标值,附上代码如下:
int maxSum_4(int * A, int n, int& first, int& second)
{
	int sum = 0;
	int max = A[0];
	int cur = first = 0;
	for(int i = 0;i < n; i++)
	{
		if(sum > 0)
			sum += A[i];
		else
		{
			sum = A[i];
			cur = i;
		}
		if(sum > max)
		{
			first = cur;
			max = sum;
			second = i;
		}
	}
	return max;
}







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值