算法学习-数据结构-2.4.3最大子序列的和问题

问题描述:求一串数列中,子序列最大和;相对应的是最小子序列和问题,类似
算法一:暴力求解,时间复杂度O(N^3),不推荐
算法二:采用“分治”策略,复杂度O(NlogN),体会思想。最大子序列和可能出现在左、中、右。
 

#include <iostream>
#include <vector>
using namespace std;
int subSeqMax(const int arr[], int n);
int subProMax(const int arr[], int startIndex, int endIndex);
template<typename T>
int length(T& arr)
{
	return sizeof(arr) / sizeof(arr[0]);
}

int main()
{
	int arr[8] = { -2, 6, -1, 5 ,4 ,-7, 2 ,3 };//答案为14
	int n = length(arr);
	int subMax = subSeqMax(arr, length(arr));
	cout << "最大和:" << subMax << endl;
	system("pause");
	return 0;
}

int subSeqMax(const int arr[], int n)
{
	int startIndex = 0;
	int endIndex = n - 1;
	int max = subProMax(arr, startIndex, endIndex);
	return max;
}

int subProMax(const int arr[], int startIndex, int endIndex)
{
	int pivot = (startIndex + endIndex) / 2;

	if (startIndex == endIndex)//退出递归的基准条件
	{
		return arr[startIndex];
	}
	int leftMax = 0, rightMax = 0;
	leftMax = subProMax(arr, startIndex, pivot);
	rightMax = subProMax(arr, pivot + 1, endIndex);
	//处理最大和在中间的情况
	int leftBorderMax = 0, rightBorderMax = 0;
	int leftBorderSum = 0, rightBorderSum = 0;
	int leftBorderIndex, rightBordIndex;
	//从基准开始,求左边最大和和右边最大和
	for (int i = pivot; i >= startIndex; i--)
	{
		leftBorderSum += arr[i];
		if (leftBorderMax < leftBorderSum)
			leftBorderMax = leftBorderSum;
	}
	for (int i = pivot + 1; i <= endIndex; i++)
	{
		rightBorderSum += arr[i];
		if (rightBorderSum > rightBorderMax)
			rightBorderMax = rightBorderSum;
	}
	//求最大的一个作为结果
	int middleMax = leftBorderMax + rightBorderMax;
	int max = 0;
	if (middleMax > leftMax)
		max = middleMax;
	else max = leftMax;

	if (max < rightMax)
		max = rightMax;
	return max;
}

算法三:比较巧妙,一个优点是只对数据进行一次扫描

int maxSubSeqSum(const int arr[], int n)
{
	int maxSum, thisSum;
	maxSum = thisSum = 0;
	for (int i = 0; i < n; i++)
	{
		thisSum += arr[i];
		if (maxSum < thisSum)
			maxSum = thisSum;
		else if (thisSum < 0)
			thisSum = 0;
	}
	return maxSum;
}

算法四:采用动态规划,复杂度O(N)
很多动态规划算法非常像数学中的递推。我们如果能找到一个合适的递推公式,就能很容易的解决问题
我们用dp[n]表示以第n个数结尾的最大连续子序列的和,于是存在以下递推公式:
dp[n] = max(0, dp[n-1]) + num[n]
仔细思考后不难发现这个递推公式是正确的,则整个问题的答案是max(dp[m]) | m∈[1, N]。C语言代码如下:
note:存放dp[n]的数组和num[]共用一个,节省了空间,但破坏了输入数组!

//动态规划
#include <stdio.h>
//N是数组长度,num是待计算的数组,放在全局区是因为可以开很大的数组
int N, num[134217728];
int main()
{
	//输入数据
	cin >> N;
	for (int i = 0; i < N; i++)
		cin >> num[i];	
	int ans = 0;
	for (int i = 0; i < N; i++)
	{
		if (num[i] > 0) 
			num[i + 1] += num[i];
		if (num[i + 1] > ans) 
			ans = num[i + 1];
	}
	cout << ans << endl;
	system("pause");
	return 0;
}

这里我们没有创建dp数组,根据递归公式的依赖关系,单独一个num数组就足以解决问题,创建一个一亿长度的数组要占用几百MB的内存!这个算法的时间复杂度是O(N)的,所以它计算一亿长度的序列也不在话下!不过你如果真的用一个这么大规模的数据来测试这个程序会很慢,因为大量的时间都耗费在程序读取数据上了!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值