小菜鸟的算法导论学习笔记——最大子数组问题(分治策略)

        最大子数组问题,指给定一个数组A,寻找该数组中的一个最大非空连续子数组,使得该子数组的各元素之和最大。

        或者进行问题变换,给定一个数组B,寻找该数组中按顺序从前往后的一个小值和大值,使得大小值的差最大。通过连续取B中元素i与元素i-1之差,即可组成数组A,从而转换为上述的最大子数组问题。

        思路:对矩阵A使用分治策略求解。

        A中的最大子数组必然属于以下三种情况之一:

        1、完全位于前半段A[low,mid];

        2、完全位于后半段A[mid+1,high];

        3、跨越中点A[i...,mid,...,j];

        同理,A[low,mid]、A[mid+1,high]的最大子数组必然属于三种情况之一....如此递归....

        对于跨越中点的第三种情况,可以采用如下思路处理:

max_subarray find_max_subarray_cross(vector<int> A, int left, int right, int mid)
{
	int sum_left = INT_MIN,sum_right = INT_MIN,sum_l = 0,sum_r = 0;
	for (int i = mid; i >= left; --i)
	{
		sum_l += A[i];
		if (sum_l > sum_left)
		{
			sum_left = sum_l;
			cross.low = i;
		}
	}
	for (int j = mid + 1; j <= right; ++j)
	{
		sum_r += A[j];
		if (sum_r > sum_right)
		{
			sum_right = sum_r;
			cross.high = j;
		}
	}
	cross.sum = sum_left + sum_right;
	return cross;
}

        这里提前定义了一个max_subarray类型的名为cross的结构体,max_subarray类型的结构体包含三个信息:最大字数组的下限、上限与子数组各个元素之和。

struct max_subarray {
	int low;
	int high;
	int sum;
};
max_subarray cross = { 0,0,0 };

        对于另外两种情况,采用递归即可。注意需要加入一个if语句判定,用来处理递归到最后数组只剩一个元素的情况。整体的代码如下:

#include <iostream>
#include <vector>
using namespace std;

struct max_subarray {
	int low;
	int high;
	int sum;
};
max_subarray cross = { 0,0,0 };

max_subarray find_max_subarray_cross(vector<int> A, int left, int right, int mid)
{
	int sum_left = INT_MIN,sum_right = INT_MIN,sum_l = 0,sum_r = 0;
	for (int i = mid; i >= left; --i)
	{
		sum_l += A[i];
		if (sum_l > sum_left)
		{
			sum_left = sum_l;
			cross.low = i;
		}
	}
	for (int j = mid + 1; j <= right; ++j)
	{
		sum_r += A[j];
		if (sum_r > sum_right)
		{
			sum_right = sum_r;
			cross.high = j;
		}
	}
	cross.sum = sum_left + sum_right;
	return cross;
}

max_subarray find_max_subarray(vector<int> A, int left, int right)
{
	if (left == right)
	{
		max_subarray left_equals_right;
		left_equals_right.low = left;
		left_equals_right.high = right;
		left_equals_right.sum = A[right];
		return left_equals_right;
	}

	else
	{
		int mid_ = (left + right) / 2;
		//int mid_j = mid_ + 1;
		max_subarray left_answer = find_max_subarray(A, left, mid_);
		max_subarray right_answer = find_max_subarray(A, mid_ + 1, right);
		max_subarray cross_answer = find_max_subarray_cross(A, left, right, mid_);
		if (left_answer.sum >= right_answer.sum && left_answer.sum >= cross_answer.sum)
		{
			max_subarray answer;
			answer.low = left_answer.low;
			answer.high = left_answer.high;
			answer.sum = left_answer.sum;
			return answer;
		}
		else if (right_answer.sum >= left_answer.sum && right_answer.sum >= cross_answer.sum)
		{
			max_subarray answer;
			answer.low = right_answer.low;
			answer.high = right_answer.high;
			answer.sum = right_answer.sum;
			return answer;
		}
		else
		{
			max_subarray answer;
			answer.low = cross_answer.low;
			answer.high = cross_answer.high;
			answer.sum = cross_answer.sum;
			return answer;

		}
	}
}

int main()
{
	int num;
	cout << "输入子数组的元素数目" << endl;
	cin >> num;
	vector<int> vector_test(num);
	cout << "依次输入子数组的各元素" << endl;
	for (int i = 0; i < num; ++i)
	{
		cin >> vector_test[i];
	}
	cout << "以下依次是该子数组的最大子数组的下限、上限及和" << endl;
	max_subarray answer_ = find_max_subarray(vector_test, 0, num - 1);
	cout << answer_.low + 1 << " " << answer_.high + 1 << " " << answer_.sum << endl;

	return 0;
}

注意事项:

1、还是像上一篇关于分治策略的文章一样,想清楚递归的思路,不要太纠结递归函数本身代表的什么含义。基本思路就是先分治,再依次解决,最后进行合并。想清楚这一点就会好很多;

2、如果不想通过定义一个结构体的方式,实现函数可以返回多个数值的情况,也可以直接定义一个void函数,不过函数的形参要多添加几个,而且注意是要引用!

参考案例:算法导论——阅读笔记 + 书内代码/课后习题C++实现_算法导论 书中代码_Lip0041的博客-CSDN博客

3、在find_max_subarray函数中,第一个else语句是int mid_=...,这个mid_不能在函数体外定义!因为如果在函数体外定义了,每次都只是对同一个参数进行复制,会丢失递归前的情况!!!如果是在函数内部定义的,虽然都叫mid_,但是它们不是同一个东西!!数据也不会丢失!务必注意!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值