0%

链表双指针:到底该走几步?

环形链表II和相交链表

环形链表

环形链表I

环形链表II

判断一个链表是否有环,最基本的双指针方法。

1
public class Solution {
2
    public boolean hasCycle(ListNode head) {
3
        //快慢指针 slow每次走1格 fast每次走2格
4
        if(head==null) return false;
5
        ListNode slow = head;
6
        if(slow.next==null) return false;
7
        ListNode fast = slow.next.next;
8
       
9
        while (fast != null) {
10
            if (slow == fast) return true;  //当快慢指针相遇 说明存在环
11
            else {
12
                slow = slow.next;
13
                if(fast.next==null) return false;
14
                fast = fast.next.next;
15
            }
16
        }
17
        //没有环的最终会推出循环
18
        return false;
19
    }
20
}

但是,如果想要判断环形链表中环的起点时,就要考虑快慢指针走了多少次。

假设从链表起点出发到环起点的距离为a,环的长度为b,现有快慢指针fastslow第一次相遇。

  • fast 走的步数是slow步数的 2倍,即f = 2s

  • fastslow多走了 n 个环的长度,即f = s + nb;( 解析: 双指针都走过a步,然后在环内绕圈直到重合,重合时 fastslow 多走环的长度整数倍 )

  • 以上两式相减得:f = 2nb, s = nb

现在考虑环的起点位置为:a+nb,所以, slow指针 位置不变 ,将fast指针重新 指向链表头部节点slowfast同时每轮向前走 a 步,两个结点再次重合,该节点即为环的起点。

1
public class Solution {
2
    public ListNode detectCycle(ListNode head) {
3
        if (head == null || head.next == null) {
4
            return null;
5
        }
6
        ListNode slow = head, fast = head;
7
        while (true) {
8
            slow = slow.next;
9
            if (fast==null||fast.next == null) {
10
                return null;
11
            } else {
12
                fast = fast.next.next;
13
            }
14
            if (slow == fast) break;
15
        }
16
        fast = head;
17
        while (slow != fast) {
18
            slow = slow.next;
19
            fast = fast.next;
20
        }
21
        return slow;
22
    }
23
}

相交链表

相交链表

找到两个单链表相交的起始节点,记链表长度分别为ab,可以考虑fast结点先走 abs(a-b) 步, 然后fastslow指针在同时往后走直到相交结点。但是获得lenAlenB需要多次遍历链表。

现在考虑如何减少遍历的次数。

设链表A的长度为a,链表B的长度为bA到相交结点的距离为c,B到相交节点的距离为d

显然可以得到两者相交链表的长度:a - c = b - d

变换一下式子得到:a + d = b + c

用一个指针从链表A出发,到末尾后就从B出发,用另一个指针从B出发,到末尾后从A出发,

当前一个指针走了a+d步数时,后一个指针走了b+c,两步数相等,即走到了相交节点。

1
public class Solution {
2
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
3
        ListNode q = headA, p = headB;
4
        while (q != p) {
5
            if (q != null) {
6
                q = q.next;
7
            } else {
8
                q = headB;
9
            }
10
            if (p != null) {
11
                p = p.next;
12
            } else {
13
                p = headA;
14
            }
15
        }
16
        return q;
17
    }
18
}