题目

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

方法一

思路

依次遍历所有节点,当一个节点的下一个节点是之前出现过的节点,说明此时形成了环,且此时的“下一个节点”即环的第一个节点。

代码

/**
 * 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) {
        // IMPORTANT: Please reset any member data you declared, as
        // the same Solution instance will be reused for each test case.
        ListNode *p=head;
        while(p)
        {
            for(ListNode *q=head; q!=p; q=q->next)
                if(q==p->next)
                    return q;
            if(p==p->next)
                return p;
            p=p->next;
        }
        return NULL;
    }
};

方法二

思路

让我们先来想想,如果甲乙两个人处在一个环形跑道上(间隔以步为单位)的任意两点,不妨设两人距离相差 N 步,两人朝同一方向,甲的速度是1步/次,乙的速度是2步/次,每当甲向前1步(此时乙向前2步),甲乙之间的距离就缩小1步,当甲刚好走了 N 步时,甲乙相遇。

所以,甲乙处在环形跑道上任意两点,当甲以1步/次速度前进,乙以2步/次的速度前进,他们最终会走到同一点。这样容易得到,下图中,当甲乙分别以1步/次,2步/次从A点出发,向环前进的时候,他两最终也会在环上的某点相遇。

现在,我们不妨设A是出发点,B是环开始的点,C是甲乙相遇的点,x为A、B之间的距离,k为B、C之间的距离,n为环的周长。

我们假设甲乙相遇时甲走的路径长度为L,则乙走过的路径长度为 2L,且:
L = x + k + a * n ----(1)
2L = x + k + b * n ----(2)
其中a,b为大于等于0的正整数。两式相减:
L = (b - a)* n ----(3)
由方程(1)(3)可以得出 x + k 是 n 的整数倍。那么甲乙分别在A,C两点,以同样的1步/次的速度前进,两人最终会在B点相遇,即环的起点相遇。(此句要仔细琢磨)

图片描述

通过上面的知识,现在我们可以这样设计算法,先让甲乙分别以1步/次,2步/次从A点出发,向环前进,当两人相遇时,让乙回到A点,重新以1步/次速度前进,而甲继续从相遇的点以1步/次速度前进,两人最终相遇的点即为环的起点。

代码

/**
 * 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) {
        if (head == NULL)
        {
            return NULL;
        }
        ListNode *p = head; //慢指针
        ListNode *q = head; //快指针
        
        do
        {
            if (p->next != NULL)
            {
                p = p->next;
            }
            else
            {
                return NULL;
            }
            
            if (q->next != NULL && q->next->next != NULL)
            {
                q = q->next->next;
            }
            else
            {
                return NULL;
            }
        }while(p != q);
        
        q = head;
        while (p != q)
        {
            p = p->next;
            q = q->next;
        }
        
        return p;
    }
};

总结

方法一的时间复杂度为O(n^2),方法二的时间复杂度为O(n)。


chenhong2018
2 声望0 粉丝