4-1.最大子数组分治法实现

这题的思想是书上的(《算法导论》),代码当然也是按照书上伪码写出的;

《算法导论》中引入这个问题是通过股票的购买与出售,经过问题转换,将前一天的当天的股票差价重新表示出来,即转为了一个最大子数组的问题,具体内容我不多说,转的内容是:

13, -3, -25, 20, -3, -16, -23, 18, 20, -7, 12, -5, -22, 15, -4, 7

找到这连续的16个数里面的连续和最大的子数组;

说一下书中的思想吧:

假定我们要寻找子数组A[low..high]的最大子数组,使用分治法意味着我们要将子数组划分为两个规模尽可能相等的子数组。也就是说,找到子数组的中央位置,比如mid,然后求解两个子数组A[low..mid]和A[mid + 1..high]。所以,A[low..high]的任何连续子数组A[i..j]所处的位置必然是三种情况之一:

1.完全位于子数组A[low..mid]中, 因此low<=i<=j<=mid;

2.完全位于子数组A[mid + 1..high]中,因此mid<=i<=j<=high;

3.跨越了中点,因此low<=i<=mid<j<=high;

因此,A[low..high]的一个最大子数组所处的位置必然是这三种情况之一。实际上,A[low..high]的一个最大子数组必然是完全位于A[low..mid]中、完全位于A[mid + 1..high]中或者跨越中点的所有子数组中和最大者。

具体实现代码:

// 最大子数组.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"

const int Infinite=-10000;

//寻找跨越最大子数组和
int FindMaxCrossSubarray(int a[],int low,int mid,int high)
{
	int leftsum=Infinite;
	int sum=0;
	for (int i=mid;i>=low;i--)//左半部分
	{
		sum+=a[i];
		if (sum>leftsum)
		{
			leftsum=sum;
		}
	}
	int rightsum=Infinite;
	sum=0;
	for (int i=mid+1;i<=high;i++)//右半部分
	{
		sum+=a[i];
		if (sum>rightsum)
		{
			rightsum=sum;
		}
	}
	return leftsum+rightsum;
}

int FindMaxSubarray(int a[],int low,int high)
{
	int leftsum,rightsum,crosssum;
	if (high==low)//仅有一个元素
	{
		return a[low];
	}
	else
	{
		int mid=(low+high)/2;
		leftsum=FindMaxSubarray(a,low,mid);//前半部分
		rightsum=FindMaxSubarray(a,mid+1,high);//后半部分
		crosssum=FindMaxCrossSubarray(a,low,mid,high);//跨越
		if (leftsum>=rightsum&&leftsum>=crosssum)
		{
			return leftsum;
		}
		else if (rightsum>=leftsum&&rightsum>=crosssum)
		{
			return rightsum;
		}
		else
		{
			return crosssum;
		}
	}
}
int _tmain(int argc, _TCHAR* argv[])
{
	int a[]={13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7};
	int length=sizeof(a)/sizeof(int);
	printf("%d\n",FindMaxSubarray(a,0,length));
	return 0;
}

posted @ 2015-05-30 18:03  程序员修练之路  阅读(437)  评论(0编辑  收藏  举报