分治法解决最大子数组问题

    算法导论中一道很有趣的算法入门题目,问题是解决最大子数组的问题,给你一个数组,要求你求出其中和最大的连续子数组,最简单的思考方式就是暴力求解,对所有的情况都进行组合然后得出最大值,但是这个复杂度是n²,为了减少复杂度,算法导论提供了一种思路,就是用分治法解决最大子数组的问题

我们首先将要求的数组从中间分开,这就意味着最大子数组的求解会分为三种情况:

一:完全位于子数组【low,mid】中。则low≤i≤j≤mid。

二:完全位于子数组【mid+1,high】中。则mid+1≤i≤j≤high

三:跨越了中点。则low≤i≤mid<j≤high。

最大子数组必然属于这三种情况,先是第三种情况,跨越了中点,这意味着子数组必然包括中点,我们只需从中点向两边扩散即可求出最大和。

	//FIND_MAX_CROSSING_SUBARRAY
	public  int FMCS(int A[],int low,int high) {//求跨越中间部分的数组
		int leftSum = -10000,rightSum = -10000;
		int mid = (low + high)/2;
		int sum = 0;
		int maxLeft = 0, maxRight = 0;
		
		for(int i = mid;i>=low;i--)
		{
			sum += A[i];
			if (sum>leftSum)
			{
				leftSum = sum;
				maxLeft = i;
			}
		}//求出左边部分的最大和
		
		sum = 0;
		
		for(int j = mid+1;j<= high;j++)
		{
			sum+=A[j];
			if(sum>rightSum) 
			{
				rightSum = sum;
				maxRight = j;
			}
		}//求出右边部分的最大和
		
		return leftSum+rightSum;
	}

接着就是分治求出最大和的问题

//FIND_MAXIMUM_SUBARRAY
	public static int FMS(int A[],int low,int high) {
		int leftSum,rightSum,crossSum;
		if(high == low)
			return A[low];//分治到只有一个元素的情况
		
		int mid = (low+high)/2;
		leftSum = FMS( A , low,  mid);
		rightSum = FMS( A, mid+1, high);递归求解前两种情况
		crossSum = FMCS( A, low, high);第三种情况
		
		
		if(leftSum>rightSum&&leftSum>crossSum)
			return leftSum;//检查最大和并进行比较来判断
		
		if(rightSum>leftSum&&rightSum>crossSum)
			return rightSum;
		
		return crossSum;
		
		
	}

}

分治法的复杂度是nlgn,但是我写的这部分有一个缺陷,我这个程序仅仅能求出最大和,但是不能求出子数组的起始位置和结束位置。

你可能感兴趣的:(ACM)