有感leetcode上的两个题:Linked List Cycle 和
Linked List Cycle II
1. Given a linked list, determine if it has a cycle in it.
2. Given a linked list, return the node where the cycle begins. If there is no cycle, return null
.
一开始用的set来做,即把已经访问过的节点放到set里,依次访问链表的各个节点,如果某个节点不在set中,将它加入到set,如果已经在了,则有环。而第一个已经在set中的节点,即为环的第一个节点。
stl的set用的是红黑树,unordered_set是用hash实现,所以后者会快一些。
如果用这个方法的话,两道题代码很通用。。。
class Solution {
public:
bool hasCycle(ListNode *head) {
set<int> s;
if(head==NULL)
return false;
ListNode* t = head;
ListNode* n = NULL;
n = new ListNode((int)head);
t = t->next;
while(1){
if(t==NULL)
return false;
else{
set<int>::iterator it = s.find((int)t);
if(it!=s.end())
return true;
s.insert((int)t);
}
t = t->next;
}
}
};
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
set<int> s;
if(head==NULL)
return NULL;
ListNode* t = head;
ListNode* n = NULL;
s.insert((int)head);
t = t->next;
while(1){
if(t==NULL)
return NULL;
else{
set<int>::iterator it = s.find((int)t);
if(it!=s.end())
return t;
s.insert((int)t);
}
t = t->next;
}
}
};
这个题目其实是典型的快慢指针的问题,一个快指针一个慢指针,如果慢指针能追上快指针,则有环,否则,无。这个是解决第一个题的方法,贴代码:
class Solution {
public:
bool hasCycle(ListNode *head) {
if(head==NULL)
return false;
ListNode* two = head;
ListNode* one = head;
while(1){
one = one->next;
two = two->next;
if(one ==NULL || two==NULL)
return false;
two = two->next;
if(one ==NULL || two==NULL)
return false;
if(one==two)
return true;
}
return true;
}
};
有了快慢指针以后,可以判断出有没有环,那么,怎么去找出环的第一个节点?
这是一个著名的龟兔赛跑问题。
假设这个链表的非环的部分长度为L,环的长度为R,相遇的位置在环的第K个节点,则有:
T = L+K+m*R
2*T = L+K+n*R
带入T则有 (n-2*m)*R=L+K
由于n-2*m 是整数,所以得出(L+K)%R=0,即L=x*R-K,则有:
此时另一个节点从head开始单步遍历,同时步长为1的节点也开始单步遍历,直到二者相遇时,即是环的第一个节点。
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if(head==NULL)
return NULL;
ListNode* one = head;
ListNode* two = head;
while(1){
one = one->next;
two = two->next;
if(one==NULL || two==NULL)
return NULL;
two = two->next;
if(two==NULL)
return NULL;
if(two==one)
break;
}
two = head;
while(two!=one){
one = one->next;
two = two->next;
}
return one;
}
};
精妙呀。