连续子数组的最大和问题及其变化

输入一个整型数组,数组中有正数也有负数。数组中的一个或连续的多个整数组成一个子数组。求所有子数组的和的最大值。
如输入{1,-2,3,10,-4,7,2,-5},和最大的子数组为{3,10,-4,7,2},输出应该是18
这是在《剑指offer》上看到的题目,这道题可以在O(n)的时间复杂度内求解,而且这个问题可以是很多更加复杂问题的子问题。所以记录加深下印象。
分析:
以上面列出的{1,-2,3,10,-4,7,2,-5}来分析,可以这么想,从头开始遍历,当前面某几项的和是负数,需要舍弃前面的。
即从1开始,此时记录到的最大和为1,然后遍历到-2,因为1+(-2)<0,所以舍弃掉-2,然后继续向后分析,3>1,所以更新最大值为3,然后继续向后遍历,3+10=13,更新最大值为13,13+(-4)=9,继续向后遍历。。。
即用两个中间变量来存储,一个用来存储遍历到当前项时最大的连续子数组之和(maxSum),另外一个用来存储包含当前项的最大子数组之和(curSum),如果curSum>maxSum,那么用curSum更新maxSum,否则继续遍历。

代码如下:

#include <stdlib.h>
#include <stdio.h>
#include <iostream>
//输入是否正确的标示符
bool f_validate=true;

int getGreatestOfSubArray(int *datas,int length)
{
	if(datas==NULL|| length<=0)
	{
		f_validate=false;
		return 0;
	}		

	int curSum=0;
	int maxSum=0x80000000;//0x80000000是最小的负整数

	for(int i=0;i<length;i++)
	{
		//如果当前和小于等于0,那么舍弃掉它,从数组中的当前项开始累加
		if(curSum<=0)
		{
			curSum=datas[i];
		}else//否则当前和需要加上数组中的当前的项
		{
			curSum+=datas[i];
		}

		//每次计算一次当前的和后都需要更新最大和
		if(curSum>maxSum)
				maxSum=curSum;
	}

	return maxSum;
}

int main(){
	int datas[]={1,-2,3,10,-4,7,2,-5};
	int max;max=getGreatestOfSubArray(datas,sizeof(datas)/sizeof(datas[0]));
	std::cout<<max<<std::endl;
	return 0;
}

之前还没注意,后来看了书之后发现可以用动态规划来分析这个问题:
设f(i)表示以第i个数字结尾的子数组的最大和,那么我们需要求的是max{f(i)},0<=i<length,f(i)可以根据下面的公式求解:
f(i)=datas[i] i=0或者f(i-1)<=0
f(i)=f(i-1)+datas[i]  i!=0或者f(i-1)>0
这个公式的含义是:当以第i-1个数字结尾的子数组中所有的数字的和小于0时,如果把这个负数与第i个数累加,得到的结果比第i个数字本身小,所以这种情况下以第i个数字结尾的子数组就是第i个数字本身。如果以第i-1个数字结尾的子数组中所有数组的和大于0,与第i个数字累加就得到以第i个数字结尾的子数组中所有数字的和。
这实际上可以当做上面分析的依据,实际上用递归来分析动态规划问题比较方便,但是编码还是基于循环来实现,就是上面的代码,其中f(i)就是curSum,max{f(i)}就是maxSum。

之前说过连续子数组的最大和可以是其它问题的子问题。下面是以前笔试遇到的问题:
假设已知一段时间内的股票价格走势,那么如果在这段时间内只可以买卖两次,并且只有当一笔交易完成后才可以做下一笔交易,那么最大收益是多少?
假定股票价格的走势以数组的形式给出:[1,4,5,2,3,1],表示第一天股票价格是1,第二天是4,第3天是5,第四天是2...(这里不考虑股票交易日限制的问题)。那么这两笔买卖可以第一天买,第二天卖,然后再第4天买,第5天买,这样第一笔收益是4-1=3,第二笔买卖收益是3-2=1.
这个问题实际上就是连续数组的最大和问题。先考虑只能进行一笔买卖的问题,1 ,4 ,5 ,2,3, 1之间的差值是3,1,-3,1,-2,对这个差值数组求最大子数组的和就是这一笔买卖最大的收益。即上面连续字数组的最大和问题。
然后考虑两笔买卖的问题,可以以上面的任意一天为分割点,将它分成两个时间段,然后分别求两个之间段的最大值,然后将这两个最大值相加。求出所有分割点的最大值后再进行比较,求出一个总的最大值。
下面是代码:

#include <stdlib.h>
#include <stdio.h>
#include <iostream>
/*计算从first到last,包括first和last之间的差的最大和*/
int getGreatest(int *datas,int first,int last);


int getGreatestSubOfArray(int * datas,int length)
{
	if(datas==NULL||length<=0) return -1;
	int curSum=0;
	int maxSum=0;
	//分割,将数组以一个基准点分割成两部分,分别求两个部分的最大和,然后再求总的最大和
	for(int i=1;i<length-2;i++)
	{
		curSum=getGreatest(datas,0,i)+getGreatest(datas,i+1,length-1);
		if(curSum>maxSum)
		{
				maxSum=curSum;
		}
	}
	return maxSum;

}

/*计算从first到last,包括first和last之间的差的最大和*/
int getGreatest(int *datas,int first,int last)
{
	if(datas==NULL||first<0||last<0||first>=last) return -1;
	int curSum=0;
	int maxSum=0;
	for(int i=first;i<last;i++)//从first到last依次遍历,计算相邻两项之间的差值,转换成了连续数组的最大和问题
	{
			if(curSum<=0)//如果当前和小于0,丢弃它
			{
				curSum=datas[i+1]-datas[i];
			}else//否则加上当前项
			{
				curSum+=datas[i+1]-datas[i];
			}

			if(curSum>maxSum)//更新最大值
			{
				maxSum=curSum;
			}
	}
	return maxSum;

}

int main(){
	int datas[]={1,4,5,2,3,1};
	int max=getGreatestSubOfArray(datas,sizeof(datas)/sizeof(datas[0]));
	std::cout<<max<<std::endl;
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值