第一类 快慢指针
快慢指针主要解决链表问题,两个指针一般都初始化指向链表的头结点,即fast = slow = head
。
前进时快指针 fast
在前,慢指针 slow
在后。即fast = fast.next.next
,slow = slow.next
。
快慢指针定理:快指针走的长度永远是慢指针走的长度的 2 倍。
例题
-
LC141题:判定链表中是否包含环。
题解:若链表含环,快指针最终会超慢指针一圈,和慢指针相遇,即说明链表含有环。
-
LC142题:返回链表开始入环的第一个节点。
题解:当快慢指针第一次相遇时,让其中任一个指针指向头节点,然后让它俩以同速前进,第二次相遇时所在的节点位置就是环的起点,此时返回任一个指针的位置都可以。设 3 个变量来表示这个链表里的几个重要长度:
M:第一次相遇的点; L:环的length X:从链表头到环的起点的长度; Y:从环的起点到 M 点的长度; Z:从 M 点到环的起点的长度。
相遇时快慢指针分别走了多少的长度呢?
快指针:X+ Y + 假设走了 k 圈 慢指针:X + Y
则由快慢指针定理得到以下表达式: 2 * (X + Y) = X + Y + kL ==> X + Y = kL
又因为 Y + Z = L,所以得 X = Z,即 从链表头到环的起点的长度 = 从 M 点到环的起点的长度。
所以当两个指针,一个从头开始走,一个从 M 点开始走时,相遇那点就是环的起点,证毕。
ListNode detectCycle(ListNode head) {
ListNode fast, slow;
fast = slow = head;
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
if(fast == slow){
break;
}
}
if(fast == null || fast.next == null){
return null;
}
slow = head;
while(slow != fast){
slow = slow.next;
fast = fast.next;
}
return slow;
}
-
LC876题:寻找链表的中间节点。
题解:快指针走的速度是慢指针速度的2倍,当快指针指向单链表的尾部null时,慢指针所指位置即为链表的中点。【快的跑完一圈,慢的所指即为中】
-
LC19题:删除链表的倒数第N个节点。
题解:让快指针先走
n
步,然后快慢指针开始同速前进。这样当快指针走到链表末尾null时,慢指针所在位置就是倒数第n
个链表节点。(1≤n≤链表长度)ListNode removeNthFromEnd(ListNode head, int n) { ListNode fast, slow; fast = slow = head; // 快指针先前进 n 步 // 注意此处写法 while (n-- > 0) { fast = fast.next; } if (fast == null) { // 如果此时快指针走到头了,说明倒数第 n 个节点就是第一个节点 return head.next; } // 让慢指针和快指针同步向前 while (fast != null && fast.next != null) { fast = fast.next; slow = slow.next; } // slow.next 就是倒数第 n 个节点,删除它 slow.next = slow.next.next; return head; }
注意:循环 n 次,除了最常用的
for (int i = 0; i < n; ++i) {...}
之外,还可以写while (n-- > 0) {...}
和while (--n >= 0) {...}
,此处可解释为执行while循环,每执行一次循环体则n减1 ,直到n=0时跳出循环。
第二类 左右指针
对撞指针
对撞指针用来解决数组问题,在数组中实际是指两个索引值,一般初始化为 left = 0, right = nums.length - 1
。此类方法有点像二分搜索。
例题
-
LC881题:每艘船可以承载的最大重量为 limit,最多可同时载两人,返回载到每一个人所需的最小船数。
题解:先将数组原地升序排序,设置一个计数器计算所需最小船数res。对撞指针分别移动,当两者之和小于等于船可以承载的重量limit时,左指针右移,否则右指针左移。在这个过程中,res都会自增加一。
int numRescueBoats(int[] people, int limit) {
if(people == null || people.length == 0) { return 0; }
Arrays.sort(people);
int i = 0;
int j = people.length - 1;
int res = 0;
while(i<=j){
if(people[i]+people[j]<=limit){
i++;
}
j--;
res++;
}
return res;
}
固定间距指针(滑动窗口)
固定间距指针是指两个指针间距相同,步长相同,可以用来解决字符串问题。此方法与滑动窗口算法相同。