LeetCode 142. Linked List Cycle II

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?

一、算法分析

设置两个指针,slow fast分别步进为1和2;如果fast变为null,那么说明无环;如果fast和slow相等,说明有环;

该类单链表的环问题一般有如下几种:

(1)判断单链表是否有环

(2)求单链表环的长度

(3)求单链表环的起始结点

(4)求单链表的总长度

解法:

(1)判断单链表是否有环,可以设置快慢指针,如果相遇了,则说明有环。下面证明为何两者一定会相遇:

假设单链表环长度为n,慢指针p 和 快指针q 同时进入环。那么经过i步之后,p位置为 i mod n ,q位置为 2i mod n 。假设两者能相遇,则有 i mod n = 2i mod n; 即 (2i - i )mod n ==0  ==>  i mod n ==0, ==> i = n。也就是说两者在起点相遇,此时慢指针正好走了一圈。这个也很好理解,两个人去操场跑步,在同一个起点开始跑,跑的快的是慢的速度的两倍。 那么当第一个人跑一圈的时候,第二个人正好跑了两圈,他们正好在起点相遇。

那么当两者不是同时进入环的话,情况会怎么样那?假设两者相差 k ,i 步之后,p位置为 i mod n ,q位置为 (2i + k)mod n 。若要两者位置相同,则需要 i mod n = (2i + k) mod n ==> (2i + k - i) mod n==0 ==> (i + k) mod n ==0  ==> i+k==n ==> i=n-k;  也就是说相遇位置在 n-k处。

(2)求环的长度

利用上面的办法找到了环里面的点,记录下该点,然后继续遍历,当相等时正好遍历环一圈,正好为环的长度

(3)求单链表环的起始结点

设头结点离环起始结点距离为k ,那么当 慢指针p 刚好走到起始结点时,快指针已经位于 环中的 k 处(因为快的是慢的速度的两倍,所以走的路程是2k)。根据(1)中的讨论,此时两者相遇的地方为 n -k 。 因此从头结点 和 碰撞点 同时走,走过k步之后,相遇的地方就是起始点。

(4)求单链表的总长度

求起始结点时,只需记录下从头结点走的步数,然后加上环的长度。

二、C语言实现

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


你可能感兴趣的:(LeetCode,算法,单链表,环)