问题描述
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?
思路分析
给一条链表,判断链表是否有环,没有就返回NULL;如果有就返回环开始的那个节点。
我们用两个指针对链表进行遍历,fast指针一次前进两个节点,slow指针一次前进一个节点。如果fast指针到了链表尾,那就说明没有环;如果在某一个节点,slow和fast相遇了,那说明有环,我们要找到环开始的节点。
这时候就需要一点小技巧了
- 令L1表示从头结点到环开始的节点的距离
- 令L2表示环开始的节点到slow和fast相遇到节点的距离。
- 令C表示环的长度。
- 令n表示fast遍历过的环次数。
因为fast的前进速度是slow的两倍,我们可知:
2(L1+L2)=L1+L2+n∗C
L1+L2=n∗C
L1=(n−1)∗C+C−L2
所以此时如果我们让begin从头出发,slow继续沿着环遍历,经过L1后到达环开始的节点时,slow和begin正好重合,我们找到了环开始的节点。
代码
/**
* 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 || !head->next)
return NULL;
ListNode* slow = head;
ListNode* fast = head;
ListNode* begin = head;
while (fast->next && fast->next->next){
slow = slow->next;
fast = fast->next->next;
if (slow == fast){
while (begin != slow){
begin = begin->next;
slow = slow->next;
}
return begin;
}
}
return NULL;
}
};
时间复杂度:
O(n)
空间复杂度:
O(1)
反思
快慢指针是处理链表中环的不二选择。对于环开始位置的判断,还是需要一定的数学推理的。