双指针算法初阶

双指针算法是一种优化循环遍历的技术,不同于简单的两层for循环。它分为快慢指针和左右指针两种类型,常用于数组和链表问题。文章通过实例介绍了如何将O(n^2)的算法优化到O(n),并提供了快慢指针解决数组重复元素问题的代码示例,强调了双指针在解决特定问题中的效率提升。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言:首先,这是不是你所了解的双指针算法?

for (int i = 0; i < n; i++)
{
	for (int j = 0; j < n; j++)
	{
		...
	}
}

那你就要继续往下看了,双指针算法可不是简单的两层的for循环暴力,这并不能起到时间优化的作用。

那话不多说,我们马上开始~~~

 

目录

 

1.双指针思想

2.双指针思想的应用

2.1快慢指针​编辑

双指针思路模板

2.2左右指针

3.金句省身


1.双指针思想

严格来说,双指针不是一种具体算法,而是一种思想,类似于贪心思想,双指针的技巧主要分为两类:快慢指针和左右指针。所谓左右指针,就是两个指针相向而行或相背而行,而快慢指针就是两个指针同向行走但是一快一慢。类似的例子有很多,比如二分算法,快排和归并排序等都利用了双指针算法。

在数组和链表中都可以采用双指针算法,本文重点来讲解在数组条件下使用双指针算法。

回到我们开头给出的代码,

for (int i = 0; i < n; i++)
{
	for (int j = 0; j < n; j++)
	{
		...
	}
}

这是一个O(n^2)的算法,而我们如果采用双指针算法,可以将上面的算法优化到O(n),我们一起来看看如何实现的:

我们可以让第二个指针在某个特定的条件下移动的次数小于n次,那么我们的代码就可以变成:

for (int i = 0; i < n; i++)
{
	while(j<n&&check(i,j))j++;//
     ...
}

这样以来内外层每次循环的次数就会小于2*n,也就是复杂度就会下降到O(n)。

2.双指针思想的应用

2.1快慢指针

 乍一看题目比较简单,我们可以定义两个指针,第一个指向起点,第二个指向终点,判断这个区间,满足就更新最大值,否则就将起点移动到下一位,继续找终点,直到找完为止。

我们不难给出代码:这里我用的STL做的,主要用于判重

#include<bits/stdc++.h>

using namespace std;
const int maxn = 1e5+10;
int a[maxn];
int mp[maxn];//表示数字的出现次数
int n;

int main()
{
	scanf("%d", &n);
	for (int i = 0; i < n; i++)
	{
		scanf("%d", &a[i]);
	}
	int ans = 1;
	for (int i = 0; i < n; i++)
	{
		int j = i;
		while (mp[a[j]] < 1 && j < n)
		{
			mp[a[j]]++;
			j++;
		}
		//printf("%d %d\n", i, j);
		memset(mp, 0, sizeof(mp));
		ans = max(ans, j - i);
	}
	printf("%d\n", ans);
	return 0;
}

但是如果给我们十万个数据,代码就要TLE了,具体的原因可以看下面的图:

 所以我们果断采用第二种实现方式,还是采用第一种空间换时间的做法,我们开一个记录当前数字个数的数组,然后确定一个终点,并从[0,i](i设为终点)中从左侧选择起点j,如果此时选择的,就一定是由于起点导致出现了重复元素,我们就让起点向终点移动,至于为什么一定是起点的问题,因为我们的终点就是从0一点点枚举变大的,所以我们在枚举出第i个符合条件的起点时,第i-1个起点已经求出并涵盖在和第i个起点内,所以一定是区间最外层的节点导致重复现象的产生。

 

那么这里便给出代码,对应可以理解下:

#include<bits/stdc++.h>

using namespace std;
const int maxn = 1e5+10;
int a[maxn];
int mp[maxn];//表示数字的出现次数
int n;

int main()
{
	scanf("%d", &n);
	for (int i = 0; i < n; i++)
	{
		scanf("%d", &a[i]);
	}
	int ans = 1;
	for (int i = 0,j=0; i < n; i++)//i指向终点,j指向起点
	{
		//首先终点要加入
		mp[a[i]]++;
		while (mp[a[i]] > 1 && j <= i) //只要是我们的a[i]出现的次数还大于1,就说明中间出现了至少一次终点的元素值,我们的起点j就需要找到这个点为止,这里由于i一定大于等于j,所以第二个条件可有可无
			--mp[a[j++]];//注意是先减再j++

		ans = max(ans, i - j + 1);
	}
	printf("%d\n", ans);
	return 0;
}

这里我们就总结给出双指针思想的一般模板思路:

双指针思路模板

for (int i = 0, j = 0; i < n; i++)
{
	//...前置条件
	while (j < i && check(j, i)) j++;//这可以将原O(n*n)的算法优化成O(n)
	
    ...//每道题目的具体逻辑

}

2.2左右指针

我们常见的二分法就是左右指针的经典应用,这里不在赘述,详见:http://t.csdn.cn/LWFvP

3.金句省身

        你一定要狠下心来努力,努力变成一个很厉害的人,没有必要让别人知道你的计划,退出不合适的圈子和不合适的人,断开不如意的感情,做最真实的自己。要成为那种不声不响,却什么都做得很好的人,早睡早起,坚持锻炼,沉下心来学习,相信点滴的努力都会有意义,努力追上那个曾将被赋予厚望的自己。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值