双指针
本页面将简要介绍双指针。
双指针是一种简单而又灵活的技巧和思想,单独使用可以轻松解决一些特定问题,和其他算法结合也能发挥多样的用处。
双指针顾名思义,就是同时使用两个指针,在序列、链表结构上指向的是位置,在树、图结构中指向的是节点,通过或同向移动,或相向移动来维护、统计信息。
接下来我们来看双指针的几个具体使用方法。
维护区间信息¶
如果不和其他数据结构结合使用,双指针维护区间信息的最简单模式就是维护具有一定单调性,新增和删去一个元素都很方便处理的信息,就比如正数的和、正整数的积等等。
例题 1 leetcode 713. 乘积小于 K 的子数组
给定一个长度为
设两个指针分别为
1 2 3 4 5 6 7 8 9 10 | int numSubarrayProductLessThanK(vector<int>& nums, int k) {
long long ji = 1ll, ans = 0;
int l = 0;
for (int i = 0; i < nums.size(); ++i) {
ji *= nums[i];
while (l <= i && ji >= k) ji /= nums[l++];
ans += i - l + 1;
}
return ans;
}
|
使用双指针维护区间信息也可以与其他数据结构比如差分、单调队列、线段树、主席树等等结合使用。另外将双指针技巧融入算法的还有莫队,莫队中将询问离线排序后,一般也都是用两个指针记录当前要处理的区间,随着指针一步步移动逐渐更新区间信息。
接下来看一道在树上使用双指针并结合树上差分的例题:
例题 2 luogu P3066 Running Away From the Barn G
给定一颗
从根开始用 dfs 遍历整棵树,使用一个栈来记录当前的这条树链,设一个指针
子序列匹配¶
例题 3 leetcode 524. 通过删除字母匹配到字典里最长单词
给定一个字符串
此类问题需要将字符串
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | string findLongestWord(string s, vector<string>& dictionary) {
sort(dictionary.begin(), dictionary.end());
int mx = 0, r = 0;
string ans = "";
for (int i = dictionary.size() - 1; i >= 0; i--) {
r = 0;
for (int j = 0; j < s.length(); ++j) {
if (s[j] == dictionary[i][r]) r++;
}
if (r == dictionary[i].length()) {
if (r >= mx) {
mx = r;
ans = dictionary[i];
}
}
}
return ans;
}
|
这种两个指针指向不同对象然后逐步进行比对的方法还可以用在一些 dp 中。
利用序列有序性¶
很多时候在序列上使用双指针之所以能够正确地达到目的,是因为序列的某些性质,最常见的就是利用序列的有序性。
例题 4 leetcode 167. 两数之和 II - 输入有序数组
给定一个已按照 升序排列 的整数数组 numbers
,请你从数组中找出两个数满足相加之和等于目标数 target
。
这种问题也是双指针的经典应用了,虽然二分也很方便,但时间复杂度上多一个
接下来介绍双指针做法:既然要找到两个数,且这两个数不能在同一位置,那其位置一定是一左一右。由于两数之和固定,那么两数之中的小数越大,大数越小。考虑到这些性质,那我们不妨从两边接近它们。
首先假定答案就是 1 和 n,如果发现
同理如果发现
推广到一般情形,如果此时我们两个指针分别指在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | vector<int> twoSum(vector<int>& numbers, int target) {
int r = numbers.size() - 1, l = 0;
vector<int> ans;
ans.clear();
while (l < r) {
if (numbers[l] + numbers[r] > target)
r--;
else if (numbers[l] + numbers[r] == target) {
ans.push_back(l + 1), ans.push_back(r + 1);
return ans;
} else
l++;
}
return ans;
}
|
在归并排序中,在
在单向链表中找环¶
在单向链表中找环也是有多种办法,不过快慢双指针方法是其中最为简洁的方法之一,接下来介绍这种方法。
首先两个指针都指向链表的头部,令一个指针一次走一步,另一个指针一次走两步,如果它们相遇了,证明有环,否则无环,时间复杂度
如果有环的话,怎么找到环的起点呢?
我们列出式子来观察一下,设相遇时,慢指针一共走了
第一次相遇时
习题¶
leetcode 1438. 绝对差不超过限制的最长连续子数组
build本页面最近更新:,更新历史
edit发现错误?想一起完善? 在 GitHub 上编辑此页!
people本页面贡献者:OI-wiki
copyright本页面的全部内容在 CC BY-SA 4.0 和 SATA 协议之条款下提供,附加条款亦可能应用