用两个指针,在数据结构中遍历和操作,以高效地解决问题。
一般有快慢指针、左右指针这2种方式。
1. 快慢指针
1.1. 有两种“快慢”方式
- 【方式1】每次迭代快指针都走1步,慢指针在满足某个条件的时候才走;
- 【方式2】每次迭代快指针都走n步(n≥2),慢指针走1步。
1.2. 常见解决的解决问题
1.2.1. 使用【方式1】解决的问题
1.2.1.1. 顺序中找重(或去重)
快指针每次都走1个节点,当快慢指针的值不一样时,慢指针走到快指针处,一样时(这时候就找到重复了)慢指针不走。
具体来说如下:
两个指针都指向排好序的数组中的第一个元素,一个for循环从第2个开始遍历,快指针指向新的元素,如果新的元素跟慢指针指向的元素值一样,慢指针就不动(这时候已经找到重复了),如果不一样,慢指针就指向快指针指向的新元素。
左手拿一个,右手拿下一个,当右手的跟左手的一样的时候,说明找到重复的了,直接把右手的扔掉,继续拿下一个,直到下一个跟左手的不一样,然后把右手的给左手,右手继续拿下一个。
1.2.1.2. 找到单链表中倒数第k个节点
快指针先走k个节点,然后快慢指针一起走,当快指针走到结尾了,慢指针就是倒数第k个节点。
我持一把k米的刀地往前冲,当刀尖(快指针)碰到墙壁时,我(慢指针)离墙(链表尾)就是k米。
1.2.1.3. 移动零
问题:把一个数组中的0移到末尾,如输入[0,1,0,3,12],输出[1,3,12,0,0]
解法:快指针每次走一步,快指针指向的元素不为0时,跟慢指针指向的值交换一下,同时慢指针走一步。快指针走过的元素,表示被处理过的元素;慢指针右边到快指针左边全是0,慢指针不一定是0。
有一排装着花的盒子,有些盒子里的花蔫了。小红和小明准备把蔫了的盒子放在一起,他们这样做:
小红和小明最开始站在第一盒花前面,小明开始从左向右一盒一盒地检查,当发现有蔫了花的盒子,小明就继续往右走,如果发现鲜花,他就开心地把这盒花跟小红的那盒换一下,同时小红站在下一盒面前。当小明走到最右边,他们的任务就做完了。
1.2.2. 使用【方式2】解决的问题
1.2.2.1. 判断链表中的环
每次快指针走2个节点、慢指针走1个节点,当两者相遇的时候,就有环,如果快指针走完全程都没跟慢指针相遇,说明没有环。
在一个环形赛道上,两人同时起跑,一个快,一个慢。快的人一定会在某个时刻追上慢的人。
1.2.2.2. 【引申问题】找到有环链表中,环的起点
这是上一个问题“判断链表中的环”引申的一个问题。解法如下:
按“判断链表中的环”的方式,快慢指针相遇的时候,再派一个新指针站在链表开头,然后这个新指针跟原来的慢指针再一起走,每次走1个节点,当他们俩相遇的时候,就是环的起点。
如下图,A是链表起点、B是环入口点、C是快慢指针相遇点,x是AB间的距离、y是BC间的距离,z是CB间的距离。根据"快指针走过的距离"=2倍"慢指针走过的距离",有x+y+z+y = 2(x+y),等式两边都减去一个x+y得到z+y=x+y,即z=x。所以新指针从A、原来的指针从C,他们一起相同速度出发,会在B点相遇,B点即环的起点。
1.2.2.3. 找到单链表中间的节点
每次快指针走2个节点、慢指针走1个节点,当快指针走完的时候,慢指针就指向的是中间的节点。
队伍中,一人正常前进,另一人两倍速前进。快的人到队尾时,慢的人正好在队伍中间。
2. 左右指针
2.1. 思路
两个指针,一个在开始位置,一个在末尾位置,根据特定条件向中间移动指针,来解决问题。
2.2. 常见解决的问题
2.2.1. 两数之和
问题:从一组数中找到相加等于某个值的两个数出来。
解法:先将这一组数排序,比如从小到大排序,左右指针分别在排序后数组的开头和结尾位置,然后计算这两个指针指向的值的和,如果和大于要求的值,右指针左移,如果和小于要求的值,左指针右移,如果相等,就找到了,如果左右指针相撞,就说明不存在这两个数。
多了就减点,左移;少了就加点,右移
2.2.2. 回文判断
左右指针分别在数组的开头和结尾位置,然后一起往中间移动,移动一次比较一次,如果左右指针的值不一样,说明不是回文。
2.2.3. 盛水最多的容器
问题:输入[1,8,6,2,5,4,8,3,7],输出49。解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为49。
解法:左右指针分别在数组的开头和结尾位置,如果固定小边,大边往中间移动,面积必然减少,所以固定大边,移动小边。