题目
这道题是 leetcode 的第 142 道题
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
对应的题目链接 环形链表2
题目解析
方法 1 :暴力 + 哈希表
暴力解法,用哈希表存储已经访问过的元素
时间复杂度:O(n)
空间复杂度:O(n)
public class Solution {
// 方法1 暴力解法+哈希表
public ListNode detectCycle(ListNode head) {
if (head == null || head.next == null) {
return null;
}
Set<ListNode> set = new HashSet<>();
while (head != null) {
if (set.contains(head)) {
return head;
}
set.add(head);
head = head.next;
}
return null;
}
方法 2:快慢指针
时间复杂度:O(n)
空间复杂度:O(1)
这种方法需要一段数学证明
下面的证明是在有环的前提下
假设从链表头到环形入口(不包括入口元素)有 a 个节点,环上有 b 个节点
用两个快慢指针从头节点开始,慢指针一次走 1 步,快指针一次走 2 步
如果有环时即快慢指针相遇
假设快指针走过的路程为 f
慢指针走过的路程为 s
因为快指针一次走2步,速度是慢指针的 2 倍,所以相遇时走过的路程也是慢指针的 2 倍 即 f = 2s
同时呢,快指针比慢指针多走了 n 倍的环的长度即 f = s + nb
那么我们根据
f = 2s
f = s + nb
可以得出 f = 2nb ,s = nb ,也就是说在相遇时,快慢指针都走了环的整数倍的长度
那想找到链表环的入口,怎么做呢?
从头节点走 a + nb 的长度就是对应的环的入口(这里可以自己画个带有环的链表感受一下)
刚好在快慢指针相遇时,慢指针已经走了 nb 的长度了,它再走 a 步就到了环的入口,刚好头结点到环的入口就是 a 步,所以在快慢指针相遇时,让头结点和慢指针一步一步的走,当他俩相遇时,慢指针对应的元素就是环的入口元素
public ListNode detectCycle(ListNode head) {
if (head == null || head.next == null) {
return null;
}
ListNode slow = head;
ListNode fast = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) {
while (head != slow) {
head = head.next;
slow = slow.next;
}
return slow;
}
}
return null;
}
}