C++函数模板求解最大子数组问题

具体算法详见《算法导论》第三版(中文)67页。

C++代码实现尽量使用数组指针代替下标,头文件程序包含求解问题的函数模板,其中没有用到一个For循环。

头文件代码如下:

#pragma once
#include <limits>
using namespace std;
namespace IntroAlgo3 
{
	template <typename T>
	struct SubArray
	{
		T *maxleft;
		T *maxright;
		T sum;
	};

	template <typename T>
	SubArray<T>& Crossing(const T *, T *, T *, T *);

	template <typename T>
	SubArray<T>& FindMaxSub(T *, T *, T *);

	template<typename T>
	SubArray<T>& Crossing(const T *A, T *low, T *mid, T *high)
	{
		T leftsum = numeric_limits<T>::min();
		T rightsum = numeric_limits<T>::min();
		T sum = 0;
		SubArray<T> *ret = new SubArray<T>;
		
		T *cursor = mid;
		while (cursor >= low)
		{
			sum += *cursor;
			if (sum > leftsum) {
				leftsum = sum;
				ret->maxleft = cursor;
			}
			--cursor;
		}
		sum = 0;
		cursor = mid+1;
		while (cursor <= high) 
		{
			sum += *cursor;
			if (sum > rightsum) {
				rightsum = sum;
				ret->maxright = cursor;
			}
			++cursor;
		}
		ret->sum = leftsum + rightsum;
		return *ret;
	}

	template<typename T>
	SubArray<T>& FindMaxSub(T *A, T *low, T *high)
	{
		T *mid;
		
		if (low == high) {
			SubArray<T> *ret=new SubArray<T>;
			ret->maxleft = low;
			ret->maxright = high;
			ret->sum = *A;
			return *ret;
		}
		else {
			SubArray<T> *left = new SubArray<T>;
			SubArray<T> *right = new SubArray<T>;
			SubArray<T> *cross = new SubArray<T>;
			mid = low+(high-low) / 2;
			*left = FindMaxSub(A, low, mid);
			*right = FindMaxSub(A, mid + 1, high);
			*cross = Crossing(A, low, mid, high);
			if (left->sum >= right->sum&&left->sum >= cross->sum) {
				return *left;
			}
			else {
				if (right->sum >= left->sum&&right->sum >= cross->sum) {
					return *right;
				}
				else {
					return *cross;
				}
			}
		}
	}

}

调用函数:

	int arr[LENGTH_ARR] = {13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7};
	for (size_t i = 0; i < LENGTH_ARR; i++)
	{
		cout << arr[i] << ",";
	}
	cout << endl;
	SubArray<int> &sa= FindMaxSub(arr, arr, arr + LENGTH_ARR-1);;
	cout << sa.maxleft - arr << ", " << sa.maxright - arr << ", " << sa.sum;//打印最大子数组元素和
	cout << endl;

结果:

[supervisor@localhost cpp]$ ./Findmax
13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7,
7, 10, 43

上述方法采用递归算法,算法复杂度O(nlogn)。另一种复杂度是线性的算法不采用递归,一次循环就可得出结果,如果n足够大,其速度可达到递归算法的几十倍甚至几百倍。

 算法的原理是,输出值初始赋值0,从下标0开始累加子数组的和,如果从头至尾各元素都是负数,则没有结果。

计算子数组的和,比较子数组和与输出值的大小,二者大的存入输出值,并确定子数组右边元素的下标。接下来下标+1,直到子数组和小于0,这时如果扫描数组没有结束,从下一个元素开始将是另一个子数组的起始。调整子数组左右下标,将temp存入左下标,当前下标赋予子数组右下标。见上表。

	template<typename T>
	SubArray<T>& FindMaxOn(T *A, T *high)
	{
		T *low = A;
		T *temp=0;
		SubArray<T> *ret = new SubArray<T>;
		ret->sum = 0;
		T thisSum = 0;
		while (low <= high) {
			thisSum += *low;//求子数组的和
			if (thisSum > ret->sum) {
				ret->sum = thisSum;//子数组的和与输出值比较,大者存入输出值
				ret->maxright = low;
				ret->maxleft = temp;
			}
			else if (thisSum < 0) {
				thisSum = 0;
				temp = low;
			}
			++low;
		}
		++ret->maxleft;//temp是在子数组的和小于零的时刻的指针,+1才是下个子数组的起始,left的值来自temp,所以这里+1.
		return *ret;
	}

 

调用:

SubArray<int> &sa= FindMaxOn(arr,  arr + LENGTH_ARR-1);

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值