leetcode
leetcode 101 ~ 150
环形链表

环形链表

难度:

标签:

题目描述

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false

 

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

 

提示:

  • 链表中节点的数目范围是 [0, 104]
  • -105 <= Node.val <= 105
  • pos-1 或者链表中的一个 有效索引

 

进阶:你能用 O(1)(即,常量)内存解决此问题吗?

代码结果

运行时间: 60 ms, 内存: 18.5 MB


/*
 * 思路:Java Stream API 不直接支持链表操作,但我们可以通过转换为 Collection 并使用 Set 来检测环。
 * 这里不使用 Stream,而是用 HashSet 来检测循环引用。
 */
import java.util.HashSet;
import java.util.Set;
 
public class Solution {
    class ListNode {
        int val;
        ListNode next;
        ListNode(int x) {
            val = x;
            next = null;
        }
    }
 
    public boolean hasCycle(ListNode head) {
        Set<ListNode> seen = new HashSet<>();
        while (head != null) {
            if (seen.contains(head)) {
                return true;
            }
            seen.add(head);
            head = head.next;
        }
        return false;
    }
}

解释

方法:

本题解使用了快慢指针的思路。初始化两个指针slow和fast,它们都指向链表的头节点。接下来,slow指针每次移动一步,fast指针每次移动两步。如果链表中存在环,那么fast和slow最终会在环中的某个节点处相遇。while循环的条件是fast和fast.next都不为None,这样可以防止fast指针越界。如果fast和slow相遇,说明链表中有环,返回True;如果fast或fast.next为None,说明到达了链表的尾部,链表中不存在环,返回False。

时间复杂度:

O(n)

空间复杂度:

O(1)

代码细节讲解

🦆
在解决此问题时,为什么选择使用快慢指针而不是其他数据结构如哈希表来检测环?
快慢指针方法是一种空间复杂度较低的解决方案,空间复杂度为O(1),因为它只需要两个指针变量。相比之下,使用哈希表来检测环需要存储已访问过的节点,这将增加额外的空间复杂度,通常为O(n),其中n是链表中的节点数。此外,快慢指针方法也可以在不修改链表结构的情况下检测环,避免了对原数据结构的干扰。
🦆
快慢指针策略中,fast指针为什么选择每次移动两步而不是三步或更多?
在快慢指针策略中,选择fast指针每次移动两步是为了保证算法的效率和正确性。如果fast指针每次移动超过两步,虽然可能提高遍历速度,但也增加了在链表中存在环的情况下错过慢指针的风险。当fast指针移动两步,而slow指针移动一步时,可以确保如果存在环,fast指针一定会在某个时刻与slow指针相遇,从而验证环的存在。此外,这种设置也有利于保证算法的简洁性和理解性。
🦆
如果快指针追上慢指针,这是否确实意味着链表中一定存在环,还是有其他可能的情况?
如果快指针追上慢指针,这确实意味着链表中一定存在环。在一个无环的链表中,快指针将会先达到链表的末尾,而不会追上慢指针。只有在链表中存在环时,快指针在绕环运行时才有可能从后方追上慢指针,因为快指针每次循环都在减少与慢指针之间的距离。
🦆
什么情况下会导致`fast.next`或`fast`变成`None`?这种情况是否表明链表一定没有环?
在没有环的链表中,`fast.next`或`fast`变成`None`的情况发生在快指针到达链表的末尾。这种情况确实表明链表一定没有环。如果存在环,快慢指针将会无限循环于环内,而不会遇到`None`。因此,当检测到`fast`或`fast.next`为`None`时,可以确定链表中不存在环。

相关问题

环形链表 II

给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos-1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

 

示例 1:

输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。

 

提示:

  • 链表中节点的数目范围在范围 [0, 104]
  • -105 <= Node.val <= 105
  • pos 的值为 -1 或者链表中的一个有效索引

 

进阶:你是否可以使用 O(1) 空间解决此题?

快乐数

编写一个算法来判断一个数 n 是不是快乐数。

「快乐数」 定义为:

  • 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。
  • 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。
  • 如果这个过程 结果为 1,那么这个数就是快乐数。

如果 n快乐数 就返回 true ;不是,则返回 false

 

示例 1:

输入:n = 19
输出:true
解释:
12 + 92 = 82
82 + 22 = 68
62 + 82 = 100
12 + 02 + 02 = 1

示例 2:

输入:n = 2
输出:false

 

提示:

  • 1 <= n <= 231 - 1