给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
说明:不允许修改给定的链表。
进阶:
你是否可以不用额外空间解决此题?
解法一
常规解法,不考虑空间复杂度,解法二用快慢指针,解决空间复杂度问题。
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode detectCycle(ListNode head) {
//set
Set<ListNode> set = new HashSet<>();
ListNode temp = head;
while (temp != null) {
//添加失败,说明回到了环的第一个节点
if (!set.add(temp)) {
return temp;
}
temp = temp.next;
}
return null;
}
}
虽然用set在入环点就返回结果了,不过中途存入set消耗了一定的时间,所以用了4ms。
解法二
快慢指针
public class Solution {
public ListNode detectCycle(ListNode head) {
//下面是做`287. 寻找重复数`时,理解的快慢指针,这道题也类似,就直接复制之前的理解笔记
//入环前的距离:a
//把环从碰撞点分割成两段,第一段是入环点到碰撞点s1,第二段碰撞点到入环点s2
//因为找到相等值值后,快指针走的是 入环前的距离 + n圈 + n+1圈的入环点到碰撞的位置(即a+n(s1+s2)+s1),
//快指针至少比慢指针多走一圈才能碰撞,这里我们可以假设快指针只走了一圈,在第二圈的某个点和慢指针碰撞了,
//那么快指针走过的距离为f=a+s1+s2+s1,而慢指针假设它一圈都没走就和快指针碰撞了,那么它走过的距离为s=a+s1,
//而快指针每次走都比多一步,是慢指针的两倍,即f=2s,替换后变为f=2a+2s1,和f的原公式相减后得出:a=s2
//其实a应该等于n(s1+s2)+s2的,不过我们并不关心走了几圈,所以可以忽略
//那么再让一个指针从头出发一次循环走一步走到入环点为a,而环中的指针每次走一步,走到入环点为s2,
//又根据上面得出的a=s2,那么它们就会在入环点再次碰撞。
ListNode fast = head, slow = head;
do {
//如果为空,说明没有环,直接返回空
if (fast == null || fast.next == null) {
return null;
}
//快指针走两步,慢指针走一步
fast = fast.next.next;
slow = slow.next;
//相等即是相遇,跳出循环
} while (fast != slow);
//快指针不动,慢指针从头重新出发
slow = head;
while (fast != slow) {
fast = fast.next;
slow = slow.next;
}
//跳出循环说明相遇了,相遇的点就是入环点,直接返回
return fast;
}
}