最大子序列求和 four solves

本文介绍了如何通过优化算法解决最大子序列和问题,从初始的O(N^3)复杂度的穷举方法逐步优化到线性的O(N)算法。解法包括递归和动态规划,并分析了算法效率和避免重复计算的重要性。

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

解法一:

穷举的尝试所有的可能

int sovleone(const int a[], int n)
{
	int thissum, sum, i, j, k;

    sum = 0;
	for (i = 0; i < n; i++)
	{
		for (j = i; j < n; j++)
		{
			thissum = 0;
			for (k = i; k <= j; k++)
			{
				thissum += a[k];
			}
			if (thissum > sum)
				sum = thissum;
		}
	}
return sum;
}

这个是书上的代码,类似便利的方法大同小异 

int nextsolveone(const int a[], int n)
{
	int thissum, sum, i, j, k;

    sum = 0;
	for (i = 0; i < n; i++)
		//每一项作为起点都要便利
	{
		for (j = 0; j + i < n; j++)
			//每一项之后J-1个数字之和,(j=0则就这一个数字)
		{
			thissum = 0;
/* 5 */			for (k = i; k - i <= j&&k < n; k++)
				//这里可能要留心,是k-i<j还是k-i<=j
			{
/* 6 */				thissum += a[k];
			}
			if (thissum > sum)
				sum = thissum;
		}
	}
return sum;
}

这两个代码相比,感觉书上的明显要整齐一点。

复杂度:O N^3

注意在第五行和第六行中(看注释),计算机会重复计算加法运算,这种方式是不好的。

比如 i=0; j = 1  和 j  = 2

在 j = 1的时候 计算机计算了  thissum += a[0]

在 j = 2的时候 计算机计算了  thissum += a[0]  thissum += a[1]

显然 thissum+=a[0]被重复计算了,,这是很不友好的行为。所以解法二的优化从此而来。

解法二:

int solvetwo(const int a[], int n)
{
	int thissum, sum, i, j;

	sum = 0;
	for (i = 0; i < n; i++)
	{
		thissum = 0;
		for (j = i; j < n; j++)
		{
			thissum += a[j];

			if (thissum > sum)
				sum = thissum;
		}
	}

	return sum;
}

这样简单的优化就使得复杂度变为 O N^2 

解法三:递归  复杂度为 N*log N

思路:最大子序列可能在三处出现,或者整个出现在数组的左半部分,或者整个出现在数组的右半部分,或者跨越数组中间从而占据左右两个部分。前两种情况可以进行递归。递归的出口条件应该是只剩余一个元素,如果该元素大于0,则该元素就为最长序列,否则返回0;

int solvethreefordigui(const int a[], int left, int right)
{
	int maxleftsum, maxrightsum;
	int maxleftbordersum, maxrightbordersum;
	int leftbordersum, rightbordersum;
	int center, i;

	if (left == right)
		//递归的出口条件,如果只有一个元素,该元素非负时它就是最大子序列
	{
		if (a[left] > 0)
			return a[left];
		else
			return 0;
    }
	
	center = (left + right) / 2;
	maxleftsum = solvethree(a, left, center);
	//在左半部分的最大值,通过递归求解。
	maxrightsum = solvethree(a, center+1, right);
	//右半部分的最大值。

	//中间部分的最大值,要求向左/右的第一位必须被包含,之后连起来。
	maxleftbordersum = 0; 
	leftbordersum = 0;
	for (i = center; i >= left; i--)
	{
		leftbordersum += a[i];
		if (leftbordersum > maxleftbordersum)
			maxleftbordersum = leftbordersum;
	}

	maxrightbordersum = 0;
	rightbordersum = 0;
	for (i = center + 1; i <= right; i++)
	{
		rightbordersum += a[i];
		if (rightbordersum > maxleftbordersum)
			maxrightbordersum = rightbordersum;
	}


	//返回左/右,中间部分的最大值。
	return max3(maxleftsum, maxrightsum, maxleftbordersum + maxrightbordersum);

}
int solvethree(const int a[], int n)
{
	return solvethreefordigui(a, 0, n - 1);
}

解法四:

似乎要放大招了

int solvefour(const int a[], int n)
{
	int thissum, maxsum, j;

	thissum = maxsum = 0;
	for (j = 0; j < n; j++)
	{
		thissum += a[j];

		if (thissum > maxsum)
			maxsum = thissum;
		else if (thissum < 0)
			thissum = 0;
	}
	return maxsum;
}

很厉害,复杂度为完全的O (N)

它只对数据进行一次扫描,一旦完成对a[j]的读入和处理,就不再需要记忆它了。并且,在读入的任何时刻,它已经对以读入的数据得出了正确的答案(假设读入前i个,则此时的maxsum就是前i个中的最长序列)。(这种算法叫联机算法。 

对算法的理解:我们从最长子序列的起点的确定来考虑;

情况如下   :     thissum     a[j+1]  a[j+2]

如果 thissum < 0   那么最长子序列的开始只能在  a[j+1]或更往后。所以应该有 thissum = 0 

(或者可以理解为:假设a[j+1]为一个正数,那么如果thissum大于0,则两个正数相加变更大,

如果thissum <0  那么 a[j+1]就不需要加上前面的thissum。加上只会变小。

对这种比较秀的算法,自己只能手动跑几个案例,来证明他是对的,目前感觉还是要多看几遍来有一点点印象吧,之后碰见才会用到这个高效的算法。。。想一瞬间就想到感觉还是比较难的。。。

留一个刚刚碰见的格言:

计算任何事情不要超过一次。

新年快乐。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值