java求最大子序列的和的问题

昨天去oracle面试,面试官很负责,先做笔试,四道题,做的不是很顺利,现在回来mark学习,革命尚未成功,同志仍需努力!加油!
import java.util.Scanner;


/**
 * @author mijing
 * @version 2015年11月24日 下午1:51:32
 */
/**
 * <p>问题:求最大子序列和: 给定一整数序列A1, A2,... An (可能有负数),求A1~An的一个子序列Ai~Aj,使得Ai到Aj的和最大 
 * <p>例如:整数序列-2, 11, -4, 13, -5, 2, -5, -3, 12, -9的最大子序列的和为21。
 *
 */
public class MaxSequenceSum {
	
	public static void main(String[] args){
		Scanner sc = new Scanner(System.in);
		String s = sc.nextLine();
		s = s.trim();
		String[] arr = s.split(",");
//		System.out.println("method2:分治法:" + method2(arr,0,arr.length - 1));
		System.out.println("method4:" + method3(arr));
	}
	/**
	 * <p>从左到右记录当前子序列的和sum,开始位置为start,结束位置为end。
	 * <p>若currentSum不断增加,那么最大子序列的和maxSum也不断增加(不断更新maxSum,start,end)。
	 * <p>如果往前扫描中遇到负数,那么当前子序列的和将会减小。此时currentSum 将会小于maxSum,当然maxSum也就不更新。
	 * <p>如果currentSum降到0时,说明前面已经扫描的那一段就可以抛弃了,这时将currentSum置为0,并且start为下一个位置。
	 * <p>然后,currentSum将从后面开始将这个子段进行分析,若有比当前maxSum大的子段,继续更新maxSum。这样一趟扫描结果也就出来了。 
	 * <p>时间复杂度为O(n);
	 * @param arr
	 * @return
	 */
	public static int method1(String[] arr){
		int maxSum = 0;
		int currentSum = 0;
		int start = 0, end = 0,  restart = 0;
		for(int i = 0; i < arr.length; i ++){
			currentSum += Integer.valueOf(arr[i]);
			if(currentSum > maxSum){
				maxSum = currentSum;
				start = restart;
				end = i;
			}
			if(currentSum <= 0){
				restart = i + 1;
				start = restart;
				currentSum = 0;
			}			
		}
		System.out.println("start:" + arr[start] + " end : "+ arr[end] + "sum: " + maxSum);
		return maxSum;
	}
	/**
	 * <p>分治法解决方案:
	 * <p>子序列的最大和只可能出现在三个位置:1、序列的左半部分;2、序列的右半部分;3、序列中横跨左右部分(一定包含中间元素)
	 * <p>1、左半部分:递归调用该函数(左半部分子串),maxLeftSum递归到left==right;
	 * <p>2、右半部分:递归调用该函数(右半部分子串)maxRightSum,递归到left==right;
	 * <p>3、中间部分:从中间元素算起,其往左走的最大和leftParMax+其往右走的最大和rightParMax,则中间结果的最大和为leftParMax + rightParMax;
	 * <p>比较maxLeftSum,maxRightSum,leftParMax + rightParMax中的最大值;
	 * <p>时间复杂度为O(nlgn)
	 * @param arr
	 * @param left
	 * @param right
	 * @return
	 */
	public static int method2(String[] arr,int left,int right){
		if(left == right)
			//空集也算是子序列,空集和为0,所以最大子序列和最小为0
			return Integer.valueOf(arr[left]) > 0 ? Integer.valueOf(arr[left]) : 0;
		int middle = (left + right) >> 1;
		int maxLeftSum = method2(arr,left,middle);
		int maxRightSum = method2(arr,middle+1, right);
		int leftSum = 0, leftParMax = 0;
		for(int i = middle; i >= left; i --){
			leftSum += Integer.valueOf(arr[i]);
			if(leftSum > leftParMax)
				leftParMax = leftSum;
		}
		int rightSum = 0, rightParMax = 0;
		for(int i = middle+1; i <= right; i ++){
			rightSum += Integer.valueOf(arr[i]);
			if(rightSum > rightParMax)
			    rightParMax = rightSum;
		}
		int temp = maxLeftSum > maxRightSum ? maxLeftSum : maxRightSum;
		return temp > (leftParMax + rightParMax) ? temp : (leftParMax + rightParMax);
	}
	/**
	 * <p>最笨的方法,三层循环:最里面的第三层循环中,从start到end计算和,最后取所有和中的最大值。
	 * <p>时间复杂度为O(n*n*n)
	 * @param arr
	 * @return
	 */
	public static int method3(String[] arr){
		int maxSum = 0;
		for(int start = 0; start < arr.length; start ++){
			for(int end = start; end < arr.length; end ++){
				int sum = 0;//当前子序列求和
				for(int i = start; i <= end; i ++){
					sum += Integer.valueOf(arr[i]);
					if(sum > maxSum)
						maxSum = sum;
				}
			}
		}
		return maxSum;
	}
	/**
	 * <p>在method3的基础上,发现在计算arr[start]到arr[end]之间的和的时候,没有必要每次都从头start处开始加,
	 * <p>可以在同一start时,上一次的end处加上该位置的end的值即可,于是可以少一层循环
	 * <p>时间复杂度为O(n*n);
	 * @param arr
	 * @return
	 */
	public static int method4(String[] arr){
		int maxSum = 0;
		for(int start = 0; start < arr.length; start ++){
			int preSum = 0;
			for(int end = start; end < arr.length; end ++){
				preSum += Integer.valueOf(arr[end]);
				if(preSum > maxSum)
					maxSum = preSum;
				
			}
		}
		return maxSum;
	}


}

参考: http://blog.csdn.net/superchanon/article/details/8228212
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值