基础高效技巧与算法(算法笔记)

一、two pointers

引例

给定一个递增的正整数序列和一个正整数M,求序列中的两个不同位置的数a和b,使得它们的和恰好为M,输出所有满足条件的方案。
最简单的方法:二重循环枚举

for(int i=0;i<n;i++)
{
	for(int j=i+1;j<n;j++)
	{
		if(a[i]+a[j]==M)
			printf("%d %d",a[i],a[j]);
	}
}

此时复杂度为O(n2),但由于序列递增,故有以下结论:
1.对一个确定的a[i],若a[i]+a[j]>M,显然后序a[i]+a[j+1]>M,则无需继续遍历
2.若a[i]+a[j]>M,很明显有a[i+1]+a[j]>M成立
我们会发现i与j这两个下标似乎互相牵制,可以给算法提供很大的优化空间
因此,针对这道题目,我们将令i初值为0,j的初值为n-1,即令i,j分别指向序列中第一个和最后一个元素,接下来根据a[i]+a[j]进行下面三种选择:
(1)如果a[i]+a[j] == M;说明已经找到了一组方案,由于序列递增,不等式a[i+1]+a[j]和a[i]+a[j+1]>M均成立,接下来要做的就是将i递增为i+1,j递减以寻找下一组满足条件的方案。
(2)如果a[i]+a[j]>M; 很明显a[i+1]+a[j]也会>M,因此需要对j递减以寻求答案
(3)如果a[i]+a[j]<M;则a[i]+a[j-1]<M,此时应递增i寻求答案
反复执行上述方案,直至i>=j成立,代码如下:

while(i<j){
	if(a[i]+a[j]==M)//找到了一组解
	{
		printf("%d %d",a[i],a[j]);
		i++;
		j--;
	}
	else if(a[i]+a[j]<M)
		i++;
	else
		j--;
}

此时复杂度变为O(n),当我们能够充分利用序列递增性质,就可以以浅显的思想降低复杂度。

数组合并

两递增序列A,B合并为一个递增序列C(这个比较基础和简单),只需要设置两个下标i和j,初值均为0,分别指向序列A和B的第一个元素,然后根据A[i]和B[j]的大小决定哪一个放入序列C,直至有一个序列的元素放完,另一个序列的剩余的元素直接加入到序列C即可

int merge(int A[], int B[], int C[],int n, int m){
	int i=0, j=0, index = 0;
	while(i<n&&j<m){
		if(A[i]<=B[j])
			C[index++] = A[i++];
		else
			C[index++] = B[j++]
	}
	while(i<n) C[index++] = A[i++];
	while(j<m) C[index++] = B[j++];
	return index;//返回序列C的长度
}

二、打表

用空间换时间的方案,以fibonacci数为例子,在递归的时候记录下已经计算出的答案,例如要求F(8),F(8)=F(6)+F(7);如果没有打表,那么计算F(7)时,F(7)=F(5)+F(6),此时又计算了一次F(6),因此我们可以通过数组记录下F(6)的数值,以便于再次用到F(6)的时候能够在短时间内解决这个问题。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值