剑指offer--简易动态规划(3、10、20、26,61)

动态规划的思想是在前一步的信息已经保存好,下一步在之前保存信息的基础上做下一步计算。


offer3

题目:输入一个整形数组,数组里有正数也有负数。数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。求所有子数组的和的最大值。要求时间复杂度为O(n)

假设之前的子数组已经得出最大和,当子数组长度向右延长1位时,子树组所有值加上延长的1位,如果和为负数,那么加上后面的连续值肯定会让和减少,需要舍弃;如果和比子数组最大和要大,那么就更新最大和,作为延长1位后的最大值。

需要注意全为负数的情况,这时需要遍历一遍数组,选出最大的负数。

代码:

bool find(int a[],int len,int& max_sum)
{
	if(a==NULL||len<=0)
		return false;
	//sum中保存当前位置时连续数字的和
	int sum=max_sum=0;
	int i;
	for(i=0; i<len; i++)
	{
		sum+=a[i];
		//新的连续值为负数,舍弃重新计算连续值
		if(sum<0)
			sum=0;
		//连续值的和比之前保存的和大,更新
		if(sum>max_sum)
			max_sum=sum;
	}

	//没有正数的情况
	if(max_sum==0)
	{
		max_sum=a[0];
		for(i=1; i<len; i++)
		{
			if(max_sum<a[i])
			{
				max_sum=a[i];
			}
		}
	}
	return true;
}


offer10

题目:输入一个已经按升序排序过的数组和一个数字,在数组中查找两个数,使得它们的和正好是输入的那个数字。要求时间复杂度是O(n)。如果有多对数字的和等于输入的数字,输出任意一对即可。

例如输入数组12471115和数字15。由于4+11=15,因此输出411

需要注意到数组已经升序排列过了,那么我们可以从两边开始选择,维持两个指针,pSmall和pBig开始的时候指向首尾,如果两个数的和大于给定值,就需左移pBig,如果小于给定值,就右移pSmall。相等就找到值了,如果最后pSmall和pBig想等了也没有找到值,那么就说明没有找到相应的值

代码:

bool FindNum(int* a, int begin, int end, int sum, int& num1, int& num2)
{
	if (a==NULL || begin>=end || begin<0 || end<0)
		return false;
	while (begin<end)
	{
		num1=a[begin];
		num2=a[end];
		if (sum == num1+num2)
			return true;
		if (sum > num1+num2)
		{
			begin++;
			continue;
		}
		if (sum < num1+num2)
		{
			end--;
			continue;
		}
	}
	return false;
}

offer20

题目:如果字符串一的所有字符按其在字符串中的顺序出现在另外一个字符串二中,则字符串一称之为字符串二的子串。注意,并不要求子串(字符串一)的字符必须连续出现在字符串二中。请编写一个函数,输入两个字符串,求它们的最长公共子串,并打印出最长公共子串。

最正宗的总台规划题目,还不太懂,先看书去





offer26

题目:输入一个正数n,输出所有和为n连续正数序列。

例如输入15,由于1+2+3+4+5=4+5+6=7+8=15,所以输出3个连续序列1-54-67-8

可以考虑设置两个指针,(最少是两个数的序列),pSmall指向 1,pBig指向 2,如果pSmallpBig之间的值小于给定值,那么需要pBig右移;如果pSmallpBig之间的值大于给定值,那么需要pSmall左移。直到超过pBig超过给定值的一半。

需要注意在对sum更新时,small是先更新和再右移,big是先右移在更新值。

代码:

void FindSum(int num)
{
	if (num<3)
		return ;

	int l=(num+1)/2;
	int small=1,big=2;
	int sum=3;

	while (small<l)
	{
		//和比给定值大,small数值右移
		if (sum > num)
		{
			sum-=small;
			small++;
		}
		//和比给定值小,big数值左移
		else if (sum < num)
		{
			big++;
			sum+=big;
		}
		//和等于给定值,打印队列。同时big右移或者samll右移,进行新一轮的寻找
		else if (sum == num)
		{
			PrintQueue(small, big);
			sum-=small;
			small++;			
		}
	}
}


offer61

在数组中,数字减去它右边的数字得到一个数对之差。求所有数对之差的最大值。例如在数组{2, 4, 1, 16, 7, 5, 11, 9}中,数对之差的最大值是11,是16减去5的结果。

类似求子数组的最大和。同样采用动态规划

假设数组长度增加1,需要增加的位是 i,由于之前我们已经找到 0~i-1中的最大差值,即 0~i-1 的每一位都作为减数测试过,新增加了第i位,我们需要知道 a[i] 作为减数的最大差值,即 a[i]之前的最大值减去a[i]的差值,然后和子数组 0~i-1中的maxdiff做比较,较大的作为子数组 0~i 的maxdiff,然后子数组长度再加1.

同时还可以用分治法,或者转换为第3题求子树组的最大和来解决。

下面是原文的解释

http://zhedahht.blog.163.com/blog/static/2541117420116135376632/

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值