算法--双指针

双指针算法介绍:

双指针算法是一种常用的算法技巧,它通常用于在数组或字符串中进行快速查找、匹配、排序或移动操作。
双指针并非真的用指针实现,一般用两个变量来表示下标(在后面都用指针来表示)

双指针算法使用两个指针在数据结构上进行迭代,并根据问题的要求移动这些指针。双指针往往也和单调性、排序联系在一起,在数组的区间问题上,暴力法的时间复杂度往往是O(n^2)的,但双指针利用“单调性”可以优化到O(n)。

双指针一般又分为对撞指针和快慢指针;两个都比较重要,所以我们一个一个详细介绍:

<1>,对撞指针:

1,对撞指针介绍

指的是两个指针 left、righ(简写为l和r)分别指向序列第一个元素和最后一个元素,然后l指针不断递增,r不断递减,直到两个指针的值相撞或错开(即1>=r),或者满足其他要求的特殊条件为止。
对撞指针一般用来解决有序数组或者字符串问题(常见于区间问题):

查找有序数组中满足某些约束条件的一组元素问题:比如二分查找、数字之和等问题

字符串反转问题:反转字符串、回文数、颠倒二进制等问题。

2,求解步骤

1.使用两个指针 left,right。left 指向序列第一个元素,即:left=1,right 指向序列最后一个元素,即:right=n。
2.在循环体中将左右指针相向移动,当满足一定条件时,将左指针右移,left++。当满足另外一定条件时,将右指针左移,right--。
3.直到两指针相撞(即left==right),或者满足其他要求的特殊条件时,跳出循环体。

下面开始举例:

eg1:lanqiao OJ 回文判定

题目分析:

  联想回文数字的特点,就是首尾相同;

//双指针
//撞指针 
//回文  lanqiao OJ 1371
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+9;
char s[N];
int main()
{
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	cin>>s+1;//重点注意这里,char类型,cin>>s+1,相当于从第一位输入而不是第0位; 
	int len=strlen(s+1);
	int l=1,r=len;
	bool ans=true;
	while(l<r&&ans)
	{
		if(s[l]!=s[r])
			ans=false;
			l++;
			r--;
	}
	cout<<(ans?'Y':'N');
	return 0;
}

对撞指针比较简单,就是两个伪指针相向移动,下面我们重点学习快慢指针;

<2>,快慢指针

1,快慢指针介绍

快慢指针一般比对撞指针更难想,也便难写;

指的是两个指针从同开始遍历序列,且移动的步长一个快一个慢;

移动快的指针被称为快指针,移动慢的指针被称为慢指针。为了方便理解,我们称快指针为r,慢指针为l,这样慢指针和快指针构成区间[l,r]。

两个指针以不同速度、不同策略移动,直到快指针移动到数组尾端,或者两指针相交,或满足其他特殊条件时为止。

2,解题步骤:

1.使用两个指针 l、r。l一般指向序列第一个元素,即:l=1,r一般指向序列第零个元素,即:r=0。即初始时区间[1,r]= [1,0]表示为空区间。

2.在循环体中将左右指针向右移动。当满足一定条件时,将慢指针右移,即l++。当满足另外一定条件时(也可能不需要满足条件),将快指针右移,即r++,保持[l,r]为合法区间。

3.到指针移动到数组尾端(即l==n且r==n),或者两指针相交,或者满足其他特殊条件时跳出循环体。

举个栗子:

eg2:lanqiao OJ 1372 美丽的区间

刚开始接触快慢指针有可能有一点不太熟练,但是会了之后还是很简单的

//快慢指针 
//双指针--lanqiao OJ 1372//美丽的区间
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+9;
int a[N],s;

int main()
{
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int n;
	cin>>n>>s;
	for(int i=1;i<=n;i++)
	cin>>a[i];
	int ans=n+1;
	for(int i=1,r=0,sum=0;i<=n;i++)//这里的i就相当于l 
	{
		while(i>r||r+1<=n&&sum<s)
		sum+=a[++r];//注意 
		if(sum>=s)
		{
			ans=min(ans,r-i+1);
			sum-=a[i];
		}
	}
    //要注意这里,如果ans>n,说明这个数组总和加起来也没有s大;;;
	cout<<(ans>n?0:ans)<<endl;
	return 0;
 } 

eg3:lanqiao OJ 1621 挑选字串

这道题和上一题几乎一样,只不过换了一种问法,建议做完上一题后自己做一下这一题

上代码:

//快慢指针
//双指针--lanqiao OJ  1621 挑选字串,和上一题差不多
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+9;
int a[N];

int main()
{
	int n,s,k;
	cin>>n>>s>>k;
	for(int i=1;i<=n;i++)
	cin>>a[i];
	int ans=0;
	for(int i=1,r=0,sum=0;i<=n;i++)//这里的i就相当于l 
	{
		while(i>r||((r+1)<=n)&&sum<k)
		sum+=(a[++r]>=s);//注意,这里用了一个逻辑表达式; 
		if(sum>=k)
		{
			ans+=n-r+1;
			sum-=(a[i]>=s);
		}
	}
	cout<<ans<<endl;
	return 0;
 }  

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值