020-142-Linked List Cycle II 判断链表是否有环并返回环的起点

Problem

Given a linked list, return the node where the cycle begins. If there is no cycle, return null.

Note: Do not modify the linked list.

Follow up:
Can you solve it without using extra space?

Solution

Actually there is a famous algorithm for this: Floyd’s cycle detection algorithm, also known as the hare-tortoise algorithm.

Floyd算法用以判断是否有环且找到环的节点。参考 维基百科-Cycle detection 。算法如下:
0. 以快慢指针的方法先判断是否有环,令慢指针一次走一步,快指针一次走两步。假设链表有环,且快慢指针在某一点相遇。
1. 令链表起点到环起点的距离为m
2. 快慢指针相遇点到环起点的距离为k
3. 设环的长度为n,令a和b分别为慢指针和快指针相遇前在环内走的圈数。
4. 慢指针到相遇点的行走的距离为i = m + a*n + k,
5. 快指针到相遇点行走的距离为2i = m + b*n + k,
6. i = 2i - i = (b - a)*n 由这个公式可以表明i为环长度的倍数
7. 那么 i = (m + k) + a*n,m + k 等于环长度的倍数,也就是说快指针距离环起点的距离是m
8. 由 7 的公式可以得到,令慢指针一步一步从链表头部开始走,快指针从相遇点开始走,他们一定会在环起点相遇(走m步)。
由上算法得到代码如下:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        bool is_circle = false;
        ListNode* slow = head;
        ListNode* fast = head;
        while(slow != NULL && fast != NULL){
            slow = slow->next;
            if(fast->next == NULL){
                break;
            }else{
                fast = fast->next->next;
            }
            if(slow == fast){
                is_circle = true;
                break;
            }
        }

        if(is_circle){
            slow = head;
            while(slow != fast){
                slow = slow->next;
                fast = fast->next;
            }
            return slow;
        }else{
            return NULL;
        }
    }
};

当m=0时,即系链表的起点为环的起点时,即系 i = m + a*n + k = a*n + k, k为n的倍数,就是说明相遇点就是环的起点。

More

如何判断环的长度?判断链表有环后,快指针不动,慢指针从环相遇点开始再走一圈直到回到相遇点,所得步数即系环的长度。

你可能感兴趣的:(leetcode)