leetcode
leetcode 2901 ~ 2950
环形链表 II

环形链表 II

难度:

标签:

题目描述

English description is not available for the problem. Please switch to Chinese.

代码结果

运行时间: 30 ms, 内存: 18.3 MB


/*
 * 思路:Floyd判圈算法的流式处理版本。 
 * 由于Stream API不适合处理具有循环结构的链表, 
 * 因此这个解法实际上不能完全通过Stream API实现。
 */
// 使用Stream API的版本不适合处理此类问题,
// 因为流处理本质上是一次性、不可逆的,不能用来处理循环链表。 
// 因此提供一种非标准的Java代码供参考:
public class Solution {
    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) {
                ListNode entry = head;
                while (entry != slow) {
                    entry = entry.next;
                    slow = slow.next;
                }
                return entry;
            }
        }
        return null;
    }
}

解释

方法:

此题解使用了快慢指针的方法来确定链表中是否有环,并找出环的入口。首先,定义两个指针,快指针每次移动两步,慢指针每次移动一步。如果链表中有环,快慢指针最终会在环内相遇。相遇后,将其中一个指针移动到链表的头部,然后两个指针都每次仅移动一步,当它们再次相遇时,相遇点即为环的入口。这个方法有效利用了数学上的环和链表结构的特性,通过两次遍历来定位环的入口。

时间复杂度:

O(n)

空间复杂度:

O(1)

代码细节讲解

🦆
为什么在快慢指针相遇之后,需要将其中一个指针重新置于链表头部,然后两个指针以相同速度移动直到再次相遇?
当快慢指针在环中相遇后,说明链表确实存在环。此时,从链表头部到环入口的距离设为D,环入口到快慢指针相遇点的距离设为S,环的剩余长度设为R。在快慢指针相遇时,慢指针走过的总距离为D + S,而快指针则是D + S + n(S + R),其中n是快指针比慢指针多绕环的圈数。由于快指针是慢指针速度的两倍,因此2(D + S) = D + S + n(S + R)。简化后得到D = n(S + R) - S = (n-1)S + nR。这表明从链表头部到环入口的距离D等于从相遇点绕环n-1圈后再走入环入口的距离。因此,将一个指针置于链表头部,另一个置于相遇点,然后两个指针以相同的速度前进,它们将在环的入口相遇。
🦆
在判断链表中是否存在环时,如果链表非常长或没有环,快指针每次移动两步是否会导致其跳过环的入口或错过慢指针的相遇?
快指针每次移动两步,而慢指针每次移动一步,不会导致快指针跳过环的入口或错过慢指针的相遇。尽管快指针移动得更快,但如果有环,快指针会在某个时间点从后面追上慢指针,因为它们都被限制在一个封闭的环中。快指针不可能跳过慢指针,因为它们每次移动的步伐是连续的。对于无环的情况,快指针的行为只会导致它更快地达到链表的尾部,并且不会影响算法的结果,因为它会在慢指针之前检测到链表的末端。
🦆
快慢指针法确定链表中环的存在时,如果环的长度非常短,例如只有一个节点,这种情况下快慢指针是否仍然有效?
即使环的长度非常短,例如只有一个节点(即自环的情况),快慢指针法仍然有效。在这种情况下,快指针和慢指针会在环中的同一节点上相遇,因为快指针每次绕环跑两步,慢指针每次跑一步。如果存在这样一个节点使得节点的next指向自己,快指针和慢指针会在该节点上相遇,从而确认环的存在。

相关问题