news 2026/6/10 1:43:06

hot100---环形链表

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
hot100---环形链表

141. 环形链表 - 力扣(LeetCode)

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

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

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

1. 方法一

思路

  • HashSet充当“便利贴集合”。

  • 遍历链表,每到一个节点,就试图把它放进HashSet(贴上便利贴)

  • 如果放进去时发现它已经存在了(已经被贴上便利贴了),说明链表有环。

  • 如果顺利走到了null,说明链表没有环。

代码

/** * Definition for singly-linked list. * class ListNode { * int val; * ListNode next; * ListNode(int x) { * val = x; * next = null; * } * } */ /** * 使用哈希表来存储所有已经访问过的节点。每次我们到达一个节点,如果该节点已经存在于哈希表中,则说明该链表是环形链表,否则就将该节点加入哈希表中。重复这一过程,直到我们遍历完整个链表即可。 */ public class Solution { public boolean hasCycle(ListNode head) { // 1. 创建一个哈希集合,用来存储“已经访问过”的节点 Set<ListNode> seen = new HashSet<>(); // 2. 遍历链表 while (head != null) { // 3. 尝试把当前节点加入集合 // add() 方法会返回 boolean: // - 如果集合中还没有这个节点,添加成功,返回 true // - 如果集合中已经有了这个节点,添加失败,返回 false if (!seen.add(head)) { // !true = false,不加;!false = true,进入 if return true; // 节点重复出现 → 有环! } // 4. 移动到下一个节点 head = head.next; } // 5. 走到链表末尾 (null) 都没发现重复节点 → 无环 return false; } }

细节补充

Set<ListNode> seen = new HashSet<>();
  • Set:一个不包含重复元素的集合。

  • HashSetSet最常用的实现,底层是哈希表,增删查平均都是 O(1)。

  • 这里存的不是节点的值,而是节点对象的引用(地址)

  • HashSet比较对象时用的是equalshashCode,默认比较内存地址,所以能精确识别出同一个节点。
    所以即使两个不同节点的值相同,也会被当成不同的节点,这正是我们需要的——我们要判断的是同一个节点被多次访问。

if (!seen.add(head))
  • add方法:如果集合中还没有这个元素,就把它加进去并返回true;如果已经有了,就什么都不做并返回false

  • 所以!seen.add(head)的意思是:如果没有添加成功(即已经存在),就执行return true,代表发现了环。

复杂度

  • 时间复杂度:O(n)
    每个节点最多访问一次(有环时,重复的那个节点被访问第二次时就会退出)。

  • 空间复杂度:O(n)
    最坏情况下需要存储所有节点的引用。


2. 方法二

思路:龟兔赛跑

  • slow(乌龟)一次走1步。

  • fast(兔子)一次走2步。

如果链表是直的(无环),兔子会先跑到终点(null),比赛结束。
如果链表中有圆圈(有环),兔子会在圈里不断跑,而且因为兔子的速度是乌龟的两倍,兔子一定会从后面追上乌龟,两者在圈内某个点相遇。

代码

public class Solution { public boolean hasCycle(ListNode head) { ListNode slow = head, fast = head; // 乌龟和兔子同时从起点出发 while (fast != null && fast.next != null) { // 兔子能跑就继续 fast = fast.next.next; // 兔子走两步 slow = slow.next; // 乌龟走一步 if (fast == slow) { // 兔子追上乌龟 → 有环 return true; } } return false; // 兔子跑到终点(null)→ 无环 } }

细节补充

  1. while (fast != null && fast.next != null)

    • fast != null:防止兔子自己踩空(当前节点不能是null)。

    • fast.next != null:防止兔子走第二步时踩空(要取fast.next.next,必须保证fast.next存在)。
      如果链表无环,兔子最先触碰到null,循环就结束了。

  2. fast = fast.next.next;
    兔子走两步,因为前面已经保证了fastfast.next非空,所以这一步安全。

  3. if (fast == slow) return true;
    比较的是两个引用是否指向同一个节点对象。如果相遇,说明兔子在环内追上了乌龟。

复杂度

  • 时间复杂度:O(n)
    无环时,兔子跑完全程;有环时,乌龟最多走一圈,兔子最多走两圈。总体上每个节点被访问常数次。

  • 空间复杂度:O(1)
    只用了两个额外指针,没有占用额外内存。


3. 相关知识补充

  1. Set接口和HashSet

    • Set不包含重复元素,允许最多一个null

    • HashSet基于哈希表,addcontainsremove都是 O(1)。

  2. add方法的返回值

    • boolean add(E e):如果集合尚未包含e,添加并返回true;如果已包含,返回false

  3. 对象的相等性

    • 默认情况下,HashSet使用hashCode()equals()来判断两个对象是否相同。

    • ListNode没有重写这两个方法,因此比较的是内存地址,正好是我们需要的“是不是同一个节点”。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/10 1:41:48

2026年宁波甄选:5家语文小升初机构全面评测

在宁波&#xff0c;每一个升学节点都像一场没有硝烟的战争。尤其是小升初这一年&#xff0c;家长们的焦虑往往比孩子还重——公办校的学区划分、民办校的摇号概率、入学分班考看不见的分数线&#xff0c;任何一点变动都牵动着全家神经。很多家长跑遍海曙、鄞州、镇海的机构&…

作者头像 李华
网站建设 2026/6/10 1:41:45

从0开始学AI测试系列-工具篇

前言 在知识星球里已经写了很多篇 AI 提效的文章&#xff0c;也搞了几次直播。 但可能之前我都是预设所有人都是有一定基础的。 所以没有从 0 开始讲解&#xff0c;这导致有些同学有点跟不上。 所以这次我决定来一个从 0 开始系列。从最基础最基础的地方开始讲起。 PS&#x…

作者头像 李华
网站建设 2026/6/10 1:39:08

python学习第十八天完结(自用)

会话列表请求方式以及路径其中返回应当返回sessions目录下的文件名模仿ai智能伴侣的格式&#xff0c;获取sessions目录下文件名&#xff0c;并将.json后缀去掉全部封装到列表中并倒序排序&#xff0c;最后返回结果成功获取列表加载指定会话前端是先加载会话列表&#xff0c;然后…

作者头像 李华
网站建设 2026/6/10 1:34:42

2026年赣州建设工程领域律师服务信息及行业情况梳理参考

2026年赣州建设工程领域律师服务信息及行业情况梳理参考2026年建设工程法律服务领域现状与需求特点近年来建设工程行业快速发展&#xff0c;对应的法律服务需求也逐步多元&#xff0c;涵盖建设工程合同纠纷、实际施工人主张工程款、企业非诉法律事务处理、职务犯罪刑事辩护等多…

作者头像 李华