快慢指针
最近在复习算法与数据结构, 一边写写博客啦。
链表辅助类
public class ListNode {
public int val;
public ListNode next;
public ListNode(int val) {
this.val = val;
}
public static ListNode createLinkedList(int[] vals) {
if (vals == null || vals.length == 0) return null;
ListNode head = new ListNode(vals[0]);
ListNode tmp = head;
for (int i = 1; i < vals.length; i++) {
tmp.next = new ListNode(vals[i]);
tmp = tmp.next;
}
return head;
}
}
快指针和慢指针
public class Main {
public static void main(String[] args) {
printFastAndSlow(ListNode.createLinkedList(new int[]{1, 2, 3, 4, 5, 6, 7}));
}
private static void printFastAndSlow(ListNode head) {
ListNode fast = head;
ListNode slow = head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
}
System.out.println("fast: " + (fast == null? "null":fast.val));
System.out.println("slow: " + (slow == null? "null":slow.val));
}
}
特性一:
快指针比慢指针来得快, 如果出现了环路, 可以迅速判断出来. 见leetcode题目: 环形链表
假设存在环,经过k个时间单位,两者相遇,fast经过路程1+2k,slow经过路程1+k。不妨设环长度为len,由相遇得(1+2k) - (1+k) = len,因为刚好超过一圈,那么k=len。如果设置计数器,可以得到环的长度。
那么环的入口在哪里呢?假设环外长度为a,在fast和slow相遇之后,在head上再放一个normal指针开始走,速度同slow指针,起始路程为1,因为在head处。此时,让原来相遇的slow指针继续以相同速度走。那么normal指针,走了a个长度之后可以到达入口处,路程为1+a;而slow指针经过的路程为1+len+a,超过normal指针一圈,也会停在入口处,此时两个指针相遇。
我有时间再补个图。
特性二:
当快指针走到终点的时候, 慢指针刚好走到中间的位置。可以自己尝试一下。设链表的长度为n:
1. head的值为null:slow值为null, fast的值为null。
2. n为奇数: slow值为中间节点(第n/2+ 1个元素), fast的值为链表的最后一个元素。
3. n为偶数: slow的值为偏右节点(第 n/2+1元素),fast的值为null。
这个特性可以用来解决回文链表的问题。 见leetcode题目: 回文链表 和 查找中间节点。处理回文链表时千万注意:fast千万放在slow前面,不然会受到slow变更影响。
class Solution {
public boolean isPalindrome(ListNode head) {
if (head == null) return true;
ListNode slow = head;
ListNode fast = head;
ListNode prev = null;
while(fast != null && fast.next != null && slow != null) {
// 注意 fast = fast.next.next一定要写在前面。
fast = fast.next.next;
ListNode next = slow.next;
slow.next = prev;
prev = slow;
slow = next;
}
// fast == null 偶数, 否则奇数。
if (fast == null) {
ListNode backward = prev;
ListNode forward = slow;
while (backward != null && forward != null) {
if (backward.val != forward.val) return false;
backward = backward.next;
forward = forward.next;
}
return true;
}
// 奇数
ListNode backward = prev;
ListNode forward = slow.next;
while (backward != null && forward != null) {
if (backward.val != forward.val) return false;
backward = backward.next;
forward = forward.next;
}
return true;
}
}