题目描述


给一个长度为n链表,若其中包含环,请找出该链表的环的入口结点,否则,返回null。

数据范围:n≤10000,1<=结点值<=10000
要求:空间复杂度 O(1),时间复杂度O(n)

例如,输入{1,2},{3,4,5}时,对应的环形链表如下图所示: alt

题解1:使用set容器

代码

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
        val(x), next(NULL) {
    }
};
*/
class Solution {
public:
    ListNode* EntryNodeOfLoop(ListNode* pHead) {
        set<ListNode*> s;
        while(pHead){
            if(s.find(pHead) == s.end()){//未查找到
                s.insert(pHead);//则将不重复的元素放入set容器中
                pHead = pHead->next;//指针向下移动
            }
            else
                return pHead;//找到重复元素,返回该结点
        }
        return pHead;//若不存在环,则返回NULL
    }
};

题解2:快慢指针

用快慢指针可以很容易判断一条链表是否存在环,快指针fast每次走两步,慢指针slow每次走一步,那么若进入环中,每次他们之间的相对距离都会-1,直到两者相遇。
假设从头节点到环的入口节点的前一个节点一共有a个,环中的节点有b个,
设fast指针走过的节点数是f,slow指针走过的节点数是s,那么有以下两个结论:
f = 2 * s (即快指针走过的节点数一定是慢指针的两倍)
f = s + nb (当两者相遇时,快指针一定已经绕环走了n圈)
由上面两个等式可以得出,f = 2nb,s = nb

算法步骤为:

在快慢指针第一次相遇前,快指针每次走两步,慢指针每次走一步 快慢指针相遇之后,快慢指针都每次仅走一步,直到再次相遇时,那个位置的结点就是环的入口结点


代码

ListNode* EntryNodeOfLoop2(ListNode* pHead) {
    ListNode* fast = pHead;
    ListNode* slow = pHead;
    while (fast)
    {
        slow = slow->next;//慢指针仅走一步
        if (fast->next == NULL) return NULL;
        fast = fast->next->next;//快指针走两步
        if (fast == slow) {//两个指针走到环形节点的同一个节点处,slow走了nb步
            fast = pHead;//快指针重新移动到头部
            while (fast !=slow)
            {//两个指针相遇,fast指向头指针之后,fast和slow每次都向后移动一步
                fast = fast->next;
                slow = slow->next;
            }//当再次相遇时候,就是环形节点的入口位置
            return fast;
        }
    }
    return NULL;
}