数据结构之最大子序列的求解

转载请注明出处:http://blog.csdn.net/droyon/article/details/8666287

随着代码量的增加,意识到写出一个工作程序并不够,如果程序在巨大的数集上运行,那么程序运行时间就变成了一个重要的问题。我们要在尚未编码的情况下比较两个程序的运行时间。以及改进程序的速度以及确定程序执行的瓶颈,然后检查程序,优化存在性能的代码段。这些都是数据结构带给我们的。

算法分析:平均运行时间反应了程序运行的典型行为,最坏运行时间则代表对所有可能输入的一种保证。

程序效率运行时间计算模型:

for循环:for循环方法体运行时间  乘以  循环次数。

嵌套for循环:里面的for循环  乘以 外面的for循环。

顺序语句:顺序语句方法体运行时间的最大值。

if/else语句:判断语句时间 加上方法体的最大时间。

我们的主角是求解最大子序列的和问题。

最大子序列的和:一系列的数字,任意连续子序列搭配,求和,找出最大和。那么,对于下面的这个数组,求最大子序列,我们有什么好的办法那?

int[] values = new int[]{4,-3,5,-2,-1,2,6, -2};
给出四种算法:

1、:穷举式的列出所有的子序列可能,在其中找到最大值。

public class SubMax1 {
	public static void main(String args[]){
		int[] values = new int[]{4,-3,5,-2,-1,2,6, -2};

		int maxSubCount = maxSub(values);
		System.out.println("最大子序列的和:"+maxSubCount);
	}

	private static int maxSub(int[] array){
		int maxSum=0;
		for(int i=0;i<array.length;i++){
			for(int j=i;j<array.length;j++){
				int sumCount=0;
				for(int k=i;k<j;k++){
					sumCount +=array[k];
				}
				if(sumCount >maxSum){
					maxSum = sumCount;
				}
			}
		}
		return maxSum;
	}
}
运行结果:

最大子序列:11
这个算法列出了所有的子序列的可能,并计算子序列的最大值。这个最大值也就是我们的所求。

时间分析:由于用了三层for循环嵌套,那么时间复杂度为:O(N的三次方)。

2、:上面的三层for循环嵌套,有一层过度设计了,我们可以优化撤除一层for循环。

public class SubMax2 {
	public static void main(String args[]){
		int[] values = new int[]{4,-3,5,-2,-1,2,6, -2};
		
		int maxSubCount = maxSub(values);
		System.out.println("最大子序列的和:"+maxSubCount);
	}
	private static int maxSub(int[] array){
		int maxSum=0;
		for(int i=0;i<array.length;i++){
			int sumCount=0;
			for(int j=i;j<array.length;j++){
				sumCount +=array[j];
				if(sumCount >maxSum){
					maxSum = sumCount;
				}
			}
		}
		return maxSum;
	}
	
}

运行结果:

最大子序列:11

同样是列出了所有的子序列,并且计算子序列的和,但这种算法的时间复杂度为O(N的二次方)
3、分治法。

把问题分成两个大致相等的子问题,然后递归地对他们求分解。这是分。

将两个问题的解修补到一起,最后得到最终答案。这是治。

最大子序列的和可能出现在三处。或者出现在输入数据的左半步,或者出现在输入数据的右半步,或者跨越左半步和右半步的子序列。前两种情况可以通过递归求解,第三种情况可以通过求出前半部分(包含前半部分的最后一个元素)的最大和 加上  后半部分(包含第一个元素)的最大和得到。  最后比较左半部分、第三种情况的值、右半部分这三个值的最大值,即为所求。

public class SubMax3 {
	public static void main(String args[]){
		int[] values = new int[]{4,-3,5,-2,-1,2,6, -2};
		
		int maxSubCount = maxSub(values, 0, values.length-1);
		System.out.println(maxSubCount+"--------------------");
	}
	public static int maxSub(int[] array,int left,int right){
		int max = 0;
		int leftCount=0;
		int rightCount = 0;
		int leftMaxCountSum=0;
		int rightMaxCountSum = 0;
		
		if(left == right){//当且只有一个元素的时候,基准情形
			if(array[left]>0)return array[left];
			return 0;
		}
		
		int center = (left+right)/2;
		int maxLeftCount = maxSub(array, left, center);
		int maxRightCount = maxSub(array, center+1, right);
		for(int i=center;i>=left;i--){
			leftCount +=array[i];
			if(leftCount>leftMaxCountSum){
				leftMaxCountSum = leftCount;
			}
		}
		
		for(int i = center+1;i<right;i++){
			rightCount += array[i];
			if(rightCount>rightMaxCountSum){
				rightMaxCountSum = rightCount;
			}
		}
		max = returnMax(maxLeftCount, maxRightCount, leftMaxCountSum+ rightMaxCountSum);
		
		return max;
	}
	public static int returnMax(int left,int right,int left_right){
		int max = 0;
		if(left>right){
			max = left;
		}else{
			max = right;
		}
		
		if(max >left_right){
			return max;
		}else{
			return left_right;
		}
	}
	
}
用到了递归,递归必须有一个基准情形。递归也是一种循环。这种算法的时间复杂度为O(NlogN)

4、时间复杂度为O(N)

public class SubMax4 {
	public static void main(String args[]){
		int[] values = new int[]{4,-3,5,-2,-1,2,6, -2};
		int maxSub = maxSub(values);
		System.out.println("最大子序列:"+maxSub);
	}
	private static int maxSub(int[] array){
		int maxCount = 0;
		int maxSub = 0;
		for(int i=0;i<array.length;i++){
			maxSub += array[i];
			if(maxSub>maxCount){
				maxCount = maxSub;
			}else if(maxSub<0){
				maxSub = 0;
			}
		}
		return maxCount;
	}
}
这个算法充分体现了编程技巧。

思想:像算法1和算法2一样,i代表程序的起点,j代表程序的终点。

如果a[i]是负的,那么它不可能成为最大子序列和的起点,因为任何包含a[i]作为起点的最大子序列都可以用a[i+1]作为起点改进。

类似地,任何负的子序列不可能是最优子序列的前缀。

如果在一个子序列a[i]到a[j]中,如果检测到i到j的子序列是负的,那么我们可以将i推进到j+1。假设p在i+1到j之间,那么开始于p到j的任意子序列不会大于下表i到p-1的任意子序列,因为i到p-1的子序列不会是负的。也就是说j是是i到j这个子序列开始成为负值的第一个下标,因而将下标i推进到j+1,是安全的,我们并不会因此错过最优解。

优点:这个算法只对数据进行一次扫描,一旦数据a[i]读入并被处理,那么他就不再需要保存。

另外这个算法,在程序读入的任意时刻,都能给予最大子序列问题的正确答案。具备这种特性的算法叫做联机算法。
















  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hailushijie

您的鼓励是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值