总结java最大连续子序列和的问题

最大连续子序列和的题目:

  给定整数A1,A2,A3,...,An(可能为负整数),求最大的连续子序列和。如果所有数是负数,则和是零。
 例如:{-2,11,-4,13,-5,2} 答案是20,序列项从第2项到第4项。
 此题很多解法,现讨论4种解法。第1种是简单穷举搜索算法,效率特别低。第2种算法是第一种算法的改进,简单观察完成。第3种算法非常高效,但不简单,复杂度是线性的O(n)。第4种算法复杂度是O(N log N)

 

 第1种算法:直接穷举搜索算法  O(n^3)

public static int maxSubSum(int[] a)
	{
		int maxSum=0,SeqStart=0,SeqEnd=0;	//记录最大和,子序列开始位置,子序列结束位置
		for(int i=0; i<a.length;i++)
		{
			for(int j=i;j<a.length;j++)
			{
				int thisSum = 0;	//记录子序列和
				for(int k=i; k<=j; k++)
				{
					thisSum +=a[k];
				}
				if( thisSum > maxSum)
				{
					maxSum = thisSum;
					SeqStart = i;
					SeqEnd = j;
				}
			}
		}		
		return maxSum;
	}

此算法最低效,最简单。


第2种算法:改进的O(n^2)算法

public static int maxSubSum(int[] a)
	{
		int maxSum=0,SeqStart=0,SeqEnd=0;	
		int i,j;
		for(i=0; i<a.length;i++)
		{
			int thisSum = 0;
			for(j=i;j<a.length;j++)
			{
				thisSum += a[j];
				if( thisSum > maxSum)
				{
					maxSum = thisSum;
					SeqStart = i;
					SeqEnd = j;
				}
			}		
		}		
		return maxSum;
	}

此算法考虑之前的子序列和进行了过多的重复运算,将子序列和重复计算值进行复用,之用2重循环,时间复杂度是O(n^2),此算法仍然是简单穷举搜索,只是略微改进而已。


第3种算法:线性算法

public static int maxSubSum(int[] a)
	{
		int maxSum=0,SeqStart=0,SeqEnd=0;
		int thisSum = 0;
		for(int i=0,j=0; j<a.length;j++)
		{
			thisSum +=a[j];
			if( thisSum > maxSum)
			{
				maxSum = thisSum;
				SeqStart = i;
				SeqEnd = j;
			}
			else if(thisSum < 0)
			{
				i = j+1;
				thisSum = 0;
			}
		}		
		return maxSum;
	}

此算法需要观察,第一次观察,前几项和不能为负数,如果是,则应该抛弃,即最大子序列不可能从负数开始,第二次观察,子序列和一旦加入负数就会小于之前的子序列和。即所有与最大连续子序列相邻的子序列的和一定为负。第三次观察,结合定理。(这个不太懂)
定理:对于任意i,设A(i,j)是第一个序列,且S(i,j)<0,那么对于任意的i<=p<=j,且p<=q,那么A(p,q)要么不是最大连续子序列和,要么等于已出现的最大连续子序列。


第4种算法:分治算法  O(N logN)

public static int maxSumRec(int[] a, int left, int right)
	{
		int maxLeftBorderSum = 0, maxRightBorderSum = 0;
		int leftBorderSum = 0, rightBorderSum = 0;
		int center = (left + right) / 2;

		if (left == right)// 基本的情况,只有一种元素
		{
			return a[left] > 0 ? a[left] : 0;
		}

		int maxLeftSum = maxSumRec(a, left, center);// 执行递归调用,计算左半部最大和
		int maxRightSum = maxSumRec(a, center + 1, right);// 执行递归调用,计算右半部分最大和

		for (int i = center; i >= left; i--)// 计算包括中心边界的左最大和
		{
			leftBorderSum += a[i];
			if (leftBorderSum > maxLeftBorderSum)
				maxLeftBorderSum = leftBorderSum;
		}

		for (int i = center + 1; i <= right; i++)// 计算包括中心边界的右最大和
		{
			rightBorderSum += a[i];
			if (rightBorderSum > maxRightBorderSum)
				maxRightBorderSum = rightBorderSum;
		}
		return max3(maxLeftSum, maxRightSum, maxLeftBorderSum
				+ maxRightBorderSum);
	}

	public static int max3(int maxLeftSum, int maxRightSum, int maxSum)//计算3个数最大的
	{
		return maxLeftSum > maxRightSum ? (maxLeftSum > maxSum ? maxLeftSum
				: maxSum) : (maxRightSum > maxSum ? maxRightSum : maxSum);
	}

	public static int maxSubsequenceSum(int[] a)
	{
		return a.length > 0 ? maxSumRec(a, 0, a.length - 1) : 0;
	}

最大子序列的和分三种情况:1)它完全在前半部分;2)它完全在后半部分;3)开始时,在前半部分,最后,在后半部分。
需要在这3种情况中,找到最大的值。运用max3()函数即可。
对于3种情况的讨论,我们使用递归运算。
1.递归计算前半部分和,2递归计算后半部分和,3通过循环计算前半部分和,后半部分和,加在一起形成第3种情况的和。(第3种情况下,前半部分和,必然是包含最后一位,后半部分和必然是包含第一位的)


参考文献:数据结构与问题求解(java语言版)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值