尺取法(又称为:双指针、two pointers),是算法竞赛中一个常用的优化技巧,用来解决序列的区间问题,操作简单、容易编程。
如果区间是单调的,也常常用二分法来求解,所以很多问题用尺取法和二分法都行。
另外,尺取法的的操作过程和分治算法的步骤很相似,有时候也用在分治中。
文章目录:
01 尺取法的概念
02 反向扫描
找指定和的整数对
判断回文串
03 同向扫描
寻找区间和
数组去重
04 典型题目
尺取法在链表中的应用
poj 3061
poj 2566
hdu 5358
洛谷p1102
uva 11572
05 参考文献
01
尺取法的概念
Q:什么是尺取法?为什么尺取法能优化呢?
A:考虑下面的应用背景:
1)给定一个序列,有时候需要它是有序的,先排序。
2)问题和序列的区间有关,且需要操作2个变量,可以用两个下标(指针)i、j扫描区间。
对于上面的应用,一般的做法,是用i、j分别扫描区间,有两重循环,复杂度O(n2)。以反向扫描(即i、j方向相反,后文有解释)为例,代码是:1for(int i = 0; i //i从头扫到尾
2 for(int j = n-1; j >= 0; j--){ //j从尾扫到头
3 ......
4 }
下面用尺取法来优化上面的算法。
实际上,尺取法就是把两重循环变成了一个循环,在这个循环中一起处理i和j。复杂度也就从O(n2)变成了O(n)。仍以上面的反向扫描为例,代码是:
1//用while实现:
2int i = 0, j = n - 1;
3while (i //i和j在中间相遇。这样做还能防止i、j越界
4 ...... //满足题意的操作
5 i++; //i从头扫到尾
6 j--; //j从尾扫到头
7}
8//用for实现:
9for (int i = 0, j = n - 1; i 10 ......
11}
在尺取法中,这两个指针i、j,有
两种扫描方向:
a)反向扫描。i、j方向相反,i从头到尾,j从尾到头,在中间相会。
b)同向扫描。i、j方向相同,都从头到尾,可以让j跑在i前面。
在leetcode的一篇文章中常用的双指针技巧
https://leetcode-cn.com/circle/article/GMopsy/,把同向扫描的i、j指针称为“快慢指针”,把反向扫描的i、j指针称为“左右指针”,更加形象。快慢指针在序列上产生了一个大小可变的“滑动窗口”,有灵活的应用,例如3.1的“寻找区间和”问题。
下文分别按双指针的反向扫描和同向扫描,给出一些经典例子。文中也列举了一些可在线提交的题目,供练习。
02
反向扫描
❶ 找指定和的整数对
这个问题是尺取法最经典,也最简单直接的应用。
问题描述输入n ( n≤100,000)个整