Given a linked list, determine if it has a cycle in it.
Follow up:
Can you solve it without using extra space?
思路:开始时,我最初的思路是利用ListNode中的val变量来当做标志变量,每当访问过一个节点之后就令 val=-1,继续往下,如果遇到val=-1 的节点,就认为是重复访问了,但是明显这种方法是不合理的,因为我们不能改变链表,也不能改变链表节点的val值。
后来在Discuss中看到了有人说用 fast runner slow runner 方法,就是利用两个指针变量slow和fast,slow一次走一步,fast一次走两步,显然如果链表存在环,那么fast一定会“追上”slow,判断是直接用这两个指针 slow==fast。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head) {
ListNode *fast=head;
ListNode *slow=head;
while(slow!=NULL&&fast!=NULL&&fast->next!=NULL){
slow=slow->next;
fast=fast->next->next;
if(slow==fast)
return true;
}
return false;
}
};
注意 NULL->next 不允许出现。
Given a linked list, return the node where the cycle begins. If there is no cycle, return null
.
Follow up:
Can you solve it without using extra space?
解法一:首先不考虑空间使用量,那么可以用一个map来存储当前遍历过的指针,一旦遇到第一个重复的,那么直接返回即可。代码如下:
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
map<ListNode*, int> mp;
ListNode *pone=head, *ptwo;
while(pone){
if(mp.count(pone))
return pone;
mp.insert(pair<ListNode*, int>(pone, 1));
pone=pone->next;
}
return NULL;
}
};
解法二:但是题目说了,本题可以用O(1)空间来解,方法很难想到,思路是同样适用两个指针遍历,一个快(p一次走两步),一个慢(q一次走一步),二者会在环中某个节点相遇,相遇时二者走过的路程相差一倍,我们设环形开始的节点为s,s距离head为x,相遇节点m距离s为y,环形长度为l,那么二者走过的路程分别为:
P=x+k*l+y
Q=x+m*l+y
且 P=2*Q,故可以推出:k*l=2*m*l+x+y,即 x+y 是 l 的整数倍,而p现在在环形的m处,m距离s为y,故p再走x步就能再次到达 s ,也即我们要找的节点处。所以再让 p 走x步就行了,而这个x的计步方法就是再让一个指针从 head出发,与p同时走,相遇时就到s了。
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
map<ListNode*, int> mp;
ListNode *pone=head, *ptwo=head;
while(pone&&ptwo){
pone=pone->next;
if(ptwo->next)
ptwo=ptwo->next->next;
else
return NULL;
if(pone==ptwo)
break;
}
if(!pone||!ptwo)
return NULL;
pone=head;
while(pone!=ptwo){
pone=pone->next;
ptwo=ptwo->next;
}
return pone;
}
};