【算法笔记】双指针及其经典例题解析

1.双指针定义

双指针时候又叫做尺取法或者滑动窗口,我们一般定义两个指针一个左指针left, 一个右指针right当条件满足时根据左右区间更新结果

有时候我们会发现不一定需要套两层for循环才能得出结果,如果使用双指针的话且指针不用回退那我们就可以使用双指针法,将我们时间复杂度由O(n ^ 2)优化为了O(n)


2.双指针经典例题

2.1唯一的雪花 Unique Snowflakes

这道题是我们双指针的模板题,我们可以首先创建一个哈希表用来保持这个元素出现的次数,当我们的右指针遍历到时,让这个元素进窗口也就是利用哈希表记录这个元素出现的次数加一,当这个区间不合法时(出现重复元素)让左指针向右指针的方向移动,左指针移动时会让左指针移动前指向的那个元素在这个区间出现的次数减一,当窗口变成重新合法时我们就更新结果,对这些结果取一个最大值即可:

cpp 复制代码
#include <iostream>
#include <unordered_map>
using namespace std;

const int N = 1e6 + 10;
int a[N];

int main()
{
	int T; cin >> T;
	while (T--)
	{
		int n; cin >> n;
		for (int i = 1; i <= n; i++) cin >> a[i];
		//初始化
		int left = 1, right = 1, ret = 0;
		unordered_map<int, int> mp;
		while (right <= n)
		{
			//记录次数 
			mp[a[right]]++;
			//判断并回到合法窗口 
			while (mp[a[right]] > 1)
			{
				mp[a[left]]--;
				left++;
			}
			ret = max(ret, right - left + 1);
			right++;
		}
		cout << ret << endl;
	}
}

2.2逛画展

其实用双指针来解决的题模板都差不多,主要是体现在进窗口和出窗口已经更新方式的不同,其余的地方还是比较的相似的

这道题的目标是在尽可能小的区间内看过所有的画,所以我们可以定义一个变量cnt用于记录这个区间内有几个画家,很明显这道题是允许区间内有重复的元素的,所以我们假设哈希表为mp,这个值为x的话,当mpx冲零变一时就是区间内有新画家的画,让cnt++,当cnt满足时更新一下结果当mpx从一变零时代表区间内少了一位画家的画,最后我们取最小的区间大小就可以了:

cpp 复制代码
#include <iostream>
#include <unordered_map>
using namespace std;
const int N = 1e6 + 10;
int a[N];
int n, m;

int main()
{
	cin >> n >> m;
	for (int i = 1; i <= n; i++) cin >> a[i];
	unordered_map<int, int> mp;
	int left = 1, right = 1;
	int cnt = 0, begin = 1; 
	int ret = n;
	while (right <= n)
	{
		if (mp[a[right]] == 0) cnt++;
		mp[a[right]]++;
		while (cnt == m)
		{
			int len = right - left + 1;
			if (len < ret)
			{
				ret = len;
				begin = left;	
			}
			if (mp[a[left]]-- == 1) cnt--;
			left++;
		}
		right++;
	}
	cout << begin << ' ' << begin + ret - 1 << endl;
	return 0;
}

2.3字符串

这道题和上一题几乎一摸一样就是换了一个条件而已了,所以这里就直接贴代码了

cpp 复制代码
#include <iostream>
#include <unordered_map>
using namespace std;
const int N = 1e6 + 10;
string s1;
unordered_map<char, int> mp;
int king;

int main()
{
	cin >> s1;
	int n = s1.size();
	int left = 0, right = 0, ret = 0x3f3f3f3f;
	while (right < n)
	{
		if (mp[s1[right]]++ == 0) king++;
		while (king == 26)
		{
			ret = min(ret, right - left + 1);
			if(mp[s1[left]]-- == 1) king--;
			left++;
		}
		right++;
	}
	cout << ret << endl;
	return 0;
}

2.4丢手绢

根据题目的条件我们可以这道小朋友之间的距离是小于总距离和sum / 2的,所以当两个小朋友的距离不合法时出窗口并更新结果在结果里取最大值即可

cpp 复制代码
#include <iostream>
using namespace std;

const int N = 1e5 + 10;
typedef long long LL;
int n;
LL a[N];

int main()
{
	LL sum = 0; cin >> n;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i];
		sum += a[i];
	}
	int left = 1, right = 1;
	LL k = 0;
	LL ret = 0;
	while (right <= n)
	{
		k += a[right];
		while (2 * k >= sum)
		{
			ret = max(ret, sum - k);
			k -= a[left++];
		}
		ret = max(ret, k);
		right++;
	}
	cout << ret << endl;
	return 0;
}

相关推荐
wabs66617 小时前
关于贪心算法的思考
算法·贪心算法
社交怪人17 小时前
【判断大小】信息学奥赛一本通C语言解法(题号1043)
算法
lengxuemo17 小时前
ICC2学习笔记之Placement and Optimization
笔记·学习
Snasph17 小时前
GNU Make 用户手册(中文版)
服务器·算法·gnu
江澎涌18 小时前
拆解与 AI 的一次对话
人工智能·算法·程序员
sheeta199818 小时前
LeetCode 每日一题笔记 日期:2026.06.02 题目:3635. 最早完成陆地和水上游乐设施的时间 II
笔记·算法·leetcode
Lsk_Smion18 小时前
力扣实训 _ [102].层序遍历--前序--后续_递归与非递归的实现
数据结构·算法·leetcode
小满Autumn19 小时前
MVVM Light 架构笔记:定位器、命令、消息与 IoC 实践
笔记·学习·架构·c#·上位机·mvvm
小欣加油20 小时前
leetcode3751 范围内总波动值I
java·数据结构·c++·算法·leetcode
代码中介商20 小时前
C++左值与右值:核心判断法则详解
开发语言·c++