题目
题意: 给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
说明:不允许修改给定的链表。
思路
如何判断链表是否有环
快慢指针法
分别定义 fast 和 slow指针,从头结点出发,fast指针每次移动两个节点,slow指针每次移动一个节点,如果 fast 和 slow指针在途中相遇 ,说明这个链表有环。
why?——
- fast指针一定先进入环中,如果fast 指针和slow指针相遇的话,一定是在环中相遇
- fast是走两步,slow是走一步,其实相对于slow来说,fast是一个节点一个节点的靠近slow的,所以fast一定可以和slow重合
如何找到环的路口
具体分析看博客
总而言之是,如果有环,那么slow和fast一定会在环内相遇点n,设环外部分为a,环的长度则为(n+b)
这时slow指针走过的路程一定是a+n(一圈内),fast走过 a+n+m(n+b),而2slow =fast。得到数学关系
所以此时再设两个指针,分别从头指针和相遇指针出发,一定会在入口汇合。
//定义一个节点在头结点处,定义一个节点在相遇的位置。两者同时移动,相遇的时候即入环节点的位置
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode* fast =head;
ListNode* slow =head;
//fast指针走得快,如果无环则最终指向NULL
//因为一次走两步,所以有可能走到末尾/直接空
while(fast!=NULL && fast->next!=NULL){
slow =slow->next;
fast =fast->next->next;
if(slow ==fast){
//相遇
ListNode* a =head;
ListNode* b =slow;
while(a!=b){
a =a->next;
b =b->next;
}
return a;
}
}
return NULL;
}
};
js 哈希表
let listSet =new Set()
let p =head
while(p){
if(listSet.has(p)){
return p
}
listSet.add(p)
p =p.next
}
return null
js双指针
var detectCycle = function(head) {
if(!head) return null
let fast=head,slow=head,cur=head
// 验证fast和fast.next非空,考虑到fast要走两步
while(fast&&fast.next){
slow =slow.next
fast =fast.next.next
if(fast ===slow){
while(cur!==slow){
cur =cur.next
slow =slow.next
}
return cur
}
}
return null
};