与环形链表I类似,区别在于需要返回环开始的第一个节点。
方法一:使用哈希表记录每个节点的指针,若遇到访问过的节点,返回该节点。
方法二:快慢指针。
快慢指针:fast
从head->next
开始,,每次移动两个节点;slow
从head
开始,每次移动一个节点。
假如链表中存在环,则fast
与slow
指针在某一时刻会在环中相遇(图中紫色点)。
此时,fast
指针移动距离:
a
+
b
+
m
(
b
+
c
)
−
1
a + b + m(b+c) - 1
a+b+m(b+c)−1,slow
指针移动距离:
a
+
b
+
n
(
b
+
c
)
a + b + n(b+c)
a+b+n(b+c)
其中,
m
m
m 与
n
n
n 均为整数,且
m
>
n
m > n
m>n。由于fast
走过的路程为slow
的两倍。由此写出方程:
a
+
b
+
m
∗
(
b
+
c
)
−
1
=
2
(
a
+
b
+
n
∗
(
b
+
c
)
)
a + b + m*(b+c) - 1 = 2(a + b + n*(b+c))
a+b+m∗(b+c)−1=2(a+b+n∗(b+c))
a
=
(
m
−
2
n
−
1
)
∗
(
b
+
c
)
+
c
−
1
a = (m-2n-1)*(b+c) + c - 1
a=(m−2n−1)∗(b+c)+c−1由等式可知,
a
a
a的距离,是整数倍的环的距离与
c
−
1
c-1
c−1的和。
如果从相遇节点(紫色点)的下一个节点开始,移动
a
a
a个节点,正好可以到达环开始的第一个节点。
因此,在快慢指针相遇时,slow
继续移动一个节点。
同时,一个新的节点从head
开始,以步长为1移动,与slow
相遇时即为环的第一个节点。
附上代码:
哈希表:
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
map<ListNode*,int> m;
while(head){
if(m.count(head)) return head;
m[head] = 1;
head = head->next;
}
return nullptr;
}
};
快慢指针:
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if(!head || !head->next) return nullptr;
ListNode* fast = head->next;
ListNode* slow = head;
while(fast != slow){
if(!fast || !slow) return nullptr;
slow = slow->next;
fast = fast->next;
if(fast) fast = fast->next;
}
ListNode* node = head;
slow = slow->next;
while(node != slow) {
slow = slow->next;
node = node->next;
}
return node;
}
};