算法-尺取法(双指针)

概念

度娘解释:尺取法,一种比较基础的算法,一般用来解决具有单调性的区间问题。通常是根据实际情况不断地推进区间左右端点以得出答案,比直接暴力枚举区间效率高很多,尤其是数据量大的时候。所以说尺取法是一种高效的枚举区间的方法,是一种技巧,一般用于求取有一定限制的区间个数或最短的区间等等。

通俗来说:给定一个序列,当数据量庞大且问题与序列的区间有关时,应用尺取法(即操作两个变量,使用一层循环扫描区间)可以优化时间复杂度O(n2)–>O(n)。

代码实现

优化前

for(int i=0;i<n;i++){//i从头扫到尾
	for(int j=n-1;j>=0;j--){//j从尾扫到头
		...
	}
}

优化后(反向扫描)

//使用for循环
for(int i=0,j=n-1;i<j;i++,j--){
	...
}
//使用while循环
int i=0,j=n-1;
while(i<j){
	...
	i++;
	j--;
}

尺取法使用时应注意以下三点:

① 尺取法的应用范围?
一般:连续区间内求解(寻找区间,数组去重,多指针问题)
② 区间的端点何时推进以及如何推进?
满足:更新
不满足:推进
根据题意判断如何推进
③ 何时结束区间的枚举?

反向扫描(左右指针)

举个栗子

题目:输入n(1e5)个整数,放在数组a[]中,找出其中的两个数,它们之和等于整数m(假设一定有解)
说明:第一行:数组a[];第二行:m
样例输入:
21 4 5 6 13 65 32 9 23
28 //m为28
样例输出:
5 23 //5+23=28

四种解决方案

  1. 二重循环暴力搜索:时间复杂度O(n2)
  2. 二分法:先从小到大排序,时间复杂度O(nlog2n)。遍历数组a[i],在大于a[i]的数中二分查找等于m-a[i]的数,时间复杂度O(nlog2n)。总时间复杂度O(nlog2n)。
  3. 哈希:分配一个哈希空间s,把n个数放进去,遍历a[]中的n个数,例a[i],检查m-a[i]在s中是否有值,时间复杂度O(n)。很快,但需要额外的很大的哈希空间。
  4. 尺取法:从小到大排序,时间复杂度O(nlog2n)。
    定义两个变量i=0,j=n-1,如果a[i]+a[j]>m,则j–,如果a[i]+a[j]<m,则i++,直至a[i]+a[j]=m,时间复杂度O(n)。总 时间复杂度O(nlog2n)。

尺取法代码实现

#include<iostream>
#include<algorithm>
using namespace std;
void find_sum(int a[],int n,int m){
	sort(a,a+n);
	int i=0,j=n-1;
	while(i<j){
		int sum=a[i]+a[j];
		if(sum>m){
			j--;
		}
		if(sum<m){
			i++;
		}
		if(sum==m){
			printf("%d %d\n",a[i],a[j]);
			i++;
		}
	}
}

完整代码

int main(){
	int a[100],n=0,m,i=0;
	while(getchar()!='\n'){
		scanf("%d",&a[i]);
		n++;
		i++;
	}
	scanf("%d",&m);
	find_sum(a,n,m);
    return 0;
}

同向扫描(快慢指针)

题目:给定长度为n的数组a[]和一个数s,在这个数组中找一个区间,使这个区间的元素之和等于s,输出区间的起点和终点。
说明:第一行:一个数n,第二行:数组a[],第三行:目标区间和s
样例输入:
15
6 1 2 3 4 6 4 2 8 9 10 11 12 13 14
6
样例输出:
0 0
1 3
5 5
6 7

定义两个变量i,j均从头部开始遍历,判断区间[i,j]数组元素的和是否为s
初始值:i=0,j=0,sum=a[0](sum为区间[i,j]数组元素和)
如果sum=s,输出一组解,sum-=a[i],i++;
如果sum>s,sum-=a[i],i++;
如果sum<s,j++,sum+=a[j];

尺取法代码实现

void find_sum(int a[],int n,int s){
	int i=0,j=0,sum=a[0];
	while(j<n){
		if(sum>=s){
			if(sum==s){
				printf("%d %d\n",i,j);
			}
			sum-=a[i];
			i++;
			if(i>j){	//防止i超过j
				sum=a[i];
				j++;
			}
		}
		if(sum<s){
			j++;
			sum+=a[j];
		}
	}
}

完整代码

int main(){
	int a[100],n,s;
	scanf("%d",&n);
	for(int i=0;i<n;i++){
		scanf("%d",&a[i]);
	}
	scanf("%d",&s);
	find_sum(a,n,s);
	return 0;
}

~~纯纯小白一枚,如果文章有错误或需要改进的地方,欢迎并感谢提出~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值