从零开始写出分而治之查找最大子列表的算法

全部每周作业和视频思考题答案和解析 见 浙江大学 数据结构 思考题+每周练习答案汇总

//寻找最大子列 分而治之算法 该算法思想可参见浙江大学陈越老师 数据结构课程

算法要求:假设有一组数列:

a[] = {2,13,2,22,1,-44.2,12,1,-21,..........}

我们目标是找到里面的最大的子列和。首先先定一个函数,参数暂且先不考虑太多,就一个数组a[]和数组的大小n

void findMaxSubSeq(int n,double a[]) {}

 

接下来思考其功能,

我们要将其分成左右两个部分,然后比较左边最大子列和右边最大子列和以及跨界最大子列和

假设数组有9个数,坐标从0到8,8/2=4 , 分为两组即0——4  5——8
假设有8个数,坐标从0到7,7/2=3,分为两组即0——3  3——7 

而分完以后,还要再分子列的子列,直到每个子列都只有一个元素位置。所以我们在分的时候需要知道该子列的最低坐标值和最大坐标值,然后求出中间值。我们修改一下该函数:

void findMaxSubSeq(int left,int right,double a[]) {}

然后思考一下函数里面的内容。首先先把中间值求出来。首先看左边部分,如果大于中心减左边大于0,则还可以继续划分,否则就返回该值。

        int center = (left+right)/2;

	if (center-left > 0) {
		//还可以继续分
	}
	else {
		//不能再继续分了
	}

这样写有点啰嗦,我们仔细想一下,这样判断左右,其实用一句话就能够成功:

	if (left == right) {
		//说明该区间里就这一个数
            //可以返回了
	}
	int center = (left+right)/2;  //left——center  center+1——right

由此可知我们的函数返回值不该是void,现在改为int

int findMaxSubSeq(int left,int right,double a[]) {
	if (left == right) {
		//说明该区间里就这一个数
		return a[left];
	}
	//如果还可以继续分,则执行下面的函数
	int center = (left+right)/2;  //left——center  center+1——right

}

现在开始考虑,我们需要通过该函数,找子列的最大子列,即需要通过递归调用。

那么现在我们如下写:

	int leftMax = findMaxSubSeq(left,center,a);
	int rightMax = findMaxSubSeq(center+1, right, a);
	//然后开始进行跨线扫描

即分别找左右两边的最大子列和,然后再找跨中间的最大子列和,最后再进行比较,然后返回当前子列的最大子列和。

现在思考怎么找跨中间的最大子列和。

我们有左右边界,还有中间值,我们先让:

int sum = a[center] + a[center + 1];

然后从中间先网左边逐步加,找找最大子列和,然后再从中间往右边加,找最大子列和:

	//然后开始进行跨线扫描
	int sum = a[center] + a[center + 1];
	int tempSum = sum;
	for (int i = center-1;i>=left;i--) {
		tempSum += a[i];
		if (tempSum > sum)sum = tempSum;
	}
	tempSum = sum;
	for (int i = center +2;i <= right;i++) {
		tempSum += a[i];
		if (tempSum > sum)sum = tempSum;
	}

这里的sum表示跨线最大子列和。然后定义了一个临时变量tempSum,用来从中间一直加到左右边界上,与sum记录的最大值进行比较。

最后我们将左边最大值和跨线最大值以及右边最大值进行比较:

	if (leftMax > sum) {
		sum = leftMax;
	}
	if (rightMax > sum)
		sum = rightMax;
	return sum;

比较三个数大小的方法多种多样,这里用了比较笨的方法,把最大值赋值给sum,然后最后返回sum。

现在贴出完整函数代码:

//寻找最大子列 分而治之算法
int findMaxSubSeq(int left,int right,int a[]) {
	//假设有9个数,8/2=4 , 0——4  5——8
	//8个数,7/2=3 0——3  3-7 

	if (left == right) {
		//说明该区间里就这一个数
		return a[left];
	}
	//如果还可以继续分,则执行下面的函数
	int center = (left+right)/2;  //left——center  center+1——right
	int leftMax = findMaxSubSeq(left,center,a);
	int rightMax = findMaxSubSeq(center+1, right, a);
	//然后开始进行跨线扫描
	int sum = a[center] + a[center + 1];
	int tempSum = sum;
	for (int i = center-1;i>=left;i--) {
		tempSum += a[i];
		if (tempSum > sum)sum = tempSum;
	}
	tempSum = sum;
	for (int i = center +2;i <= right;i++) {
		tempSum += a[i];
		if (tempSum > sum)sum = tempSum;
	}
	if (leftMax > sum) {
		sum = leftMax;
	}
	if (rightMax > sum)
		sum = rightMax;
	return sum;

}

然后我们进行验证:

	int b[10] = { 1,2,-3,4,5,6,7,-8,9,-10 };
	int xx = findMaxSubSeq(0,9,b);
	printf("xx = %d",xx);

最后打印23,是正确的。大家可以再测试一些数组验证正确性。

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Dezeming

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值