142. Linked List Cycle II

你快乐吗?

Posted by bbkgl on September 26, 2019

142. Linked List Cycle II

C++,链表,数学题。

链表的操作我们就不说了,这道题其实是一道数学题。

解法如下:

  1. 首先分别定义两个指针,一个一次走两步,一个走一步,分别为fast,slow,记录下快指针经过的结点数(不包括头结点)为lf,慢指针为ls,必有lf = 2ls
  2. 假设有环链表的长度为l,环的长度为c,从头结点到环入口的距离为a;
  3. 一个走得快,一个走得慢,且路程中有环,则必定会相遇,且lf - ls = c,设相遇时的点距离环入口为x;
  4. 根据以上变量定义和关系,我们可以得出下列等式:
    • 头结点到环入口的距离+环的长度-1=链表长,这个大家画图就明白了,即l = a + c - 1 (1)
    • 快指针走过的距离=链表长+1+相遇点到环入口的距离,画图就知道了,即lf = l + 1 + x (2)
    • 慢指针走过的距离=头结点到环入口的距离+相遇点到环入口的距离,这个显然,即ls = a + x (3)
    • 结合式(2),式(3),且lf = 2ls,所以l = 2a + x - 1 (4)
    • 联立式(4)和式(1),得到a = c - x (5)
  5. 式(5)用人话说就是,头结点到到环入口的距离 = 环长 - 相遇点到环入口的距离;
  6. 也就是让慢指针从头结点开始走,让快指针从相遇点开始走,都是一次走一步,下次相遇点就是环入口。。。

解决了数学问题,写代码就很简单了,上述思路也包含了如何判断链表有环。。。 评论区有个更骚的操作,一次遍历就行了,利用了堆上内存必定由大到小的特点。即第二次到达环入口结点时,其地址一定比前一个结点小。。。

代码如下:

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode* slow = head;
        ListNode* fast = head;
        while (slow) {
            slow = slow->next;
            fast = fast->next;
            if (!fast) return nullptr;
            fast = fast->next;
            if (!fast) return nullptr;
            if (fast == slow) break;
        }
        if (!slow) return nullptr;
        slow = head;
        while (slow != fast) {
            slow = slow->next;
            fast = fast->next;
        }
        return slow;
    }
};