给定一个链表的头节点
head,返回链表开始入环的第一个节点。 如果链表无环,则返回null
这个题目有一种很简单的方法,就是直接利用set保存走过的路径,也就是经过的所有的顶点,一但发现走过重复的顶点就直接返回该顶点即可。
public ListNode1 detectCycle(ListNode1 head) {
if (head == null || head.next == null) return null;
Set<ListNode1> visited = new HashSet<>();
while (head != null){
if (visited.contains(head)){
return head;
}
visited.add(head);
head = head.next;
}
return null;
}
时间复杂度为O(n)也就是遍历一遍链表,空间复杂度为O(n)。
但是题目进阶要求时间复杂度为O(1),且不能改变链表,那么就只能考虑双指针(快慢指针方法)。解题思想就使用官方leetcode题解。
当f第一次遇到s,那么就设置f已经走了n圈,且走了环的b长度,链表起始点到入环点为a,那么就把f没有环剩下的距离设为c,那么f就走了a+(b+c)n+b;而且f还走了两倍的s也就是2(a+b),将二者相等就会得到a=c+(n-1)(b+c) => a=c(因为s和f肯定第一次遇见就在s第一次进入环中), 那么就代表需要求出的a就是s走完接下来一圈的步数,也就是重新定义一个“s”指针指向head,当它与s指针进行相遇那么该节点就是入环点。
代码如下:
//你是否可以使用 O(1) 空间解决此题?
public ListNode1 detectCycle1(ListNode1 head) {
if (head == null && head.next == null) return null;
ListNode1 s = head;
ListNode1 f = head;
while (f.next != null && f.next.next != null){
s = s.next;
f = f.next.next;
if (s == f) break;
}
if (f.next == null || f.next.next == null) return null;
ListNode1 pre = head;
while (pre != s){
pre = pre.next;
s = s.next;
}
return pre;
}