当前位置: 首页 > news >正文

LeetCode hot 100—环形链表 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
解释:链表中没有环。

分析

哈希表法

遍历链表中的每个节点并记录下来。一旦遇到了此前遍历过的节点,就可以判定链表中存在环,直接返回该节点。

时间复杂度:O(n),n为链表中节点的数目

空间复杂度:O(n)

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        // 使用哈希集合来存储已经访问过的节点
        std::unordered_set<ListNode*> visited;
        // 遍历链表
        while (head != nullptr) {
            // 检查当前节点是否已经在哈希集合中
            if (visited.count(head)) {
                // 如果存在,说明找到了环的入口节点,返回该节点
                return head;
            }
            // 将当前节点插入到哈希集合中
            visited.insert(head);
            // 移动到下一个节点
            head = head->next;
        }
        // 如果遍历完整个链表都没有找到环,返回 nullptr
        return nullptr;
    }
};

快慢指针法

判断是否有环:首先使用快慢指针判断链表中是否存在环。slow 指针每次移动一步,fast 指针每次移动两步,如果它们相遇,则说明链表中存在环。

寻找环的入口:当快慢指针相遇后,将 slow 指针重新指向链表头节点,然后 slow 和 fast 指针以相同的速度(每次移动一步)继续移动,它们再次相遇的节点就是环的入口节点。

数学推导

设链表的头节点为 head,链表中环的入口节点为 entry,从链表头到环入口的节点数为 a,从环入口到快慢指针相遇点的节点数为 b,从快慢指针相遇点再到环入口的节点数为 c,则环的长度为 b + c

慢指针 slow 每次移动一步,快指针 fast 每次移动两步。当快慢指针相遇时,假设慢指针移动了 a + b 步,快指针移动了 y 步。因为快指针速度是慢指针的两倍,所以 y = 2(a+b)

又因为快指针比慢指针多走了若干圈环,设快指针在环里走了 n 圈(n 为正整数),则有 y = a + n(b + c) + b。将 y = 2(a+b) 代入 y = a + n(b + c) + b 可得:a = c + (n - 1)(b + c)

于是从相遇点到入环点的距离加上 n−1 圈的环长,恰好等于从链表头部到入环点的距离。即当 slow 和 fast 再次相遇时,相遇的节点就是环的入口节点

时间复杂度:O(n),n为链表中节点的数目

空间复杂度:O(1)

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        // 如果链表为空或者只有一个节点,肯定不存在环,返回 nullptr
        if (head == nullptr || head->next == nullptr) {
            return nullptr;
        }
        // 定义快慢指针
        ListNode* slow = head;
        ListNode* fast = head;
        // 标记是否有环
        bool hasCycle = false;
        // 移动快慢指针,快指针每次移动两步,慢指针每次移动一步
        while (fast != nullptr && fast->next != nullptr) {
            slow = slow->next;
            fast = fast->next->next;
            // 如果快慢指针相遇,说明存在环
            if (slow == fast) {
                hasCycle = true;
                break;
            }
        }
        // 如果没有环,返回 nullptr
        if (!hasCycle) {
            return nullptr;
        }
        // 让慢指针回到头节点
        slow = head;
        // 快慢指针以相同速度移动,再次相遇的节点就是环的入口节点
        while (slow != fast) {
            slow = slow->next;
            fast = fast->next;
        }
        return slow;
    }
};

相关文章:

  • Qt中txt文件输出为PDF格式
  • 嵌入式学习-EXTI外部中断
  • Java UDP 通信:实现简单的 Echo 服务器与客户端
  • R JSON 文件
  • 私有化部署DeepSeek并SpringBoot集成使用(附UI界面使用教程-支持语音、图片)
  • 石基大商:OceanBase + Flink CDC,搭建连锁零售系统数据湖
  • IDEA 接入 Deepseek
  • comfyui使用ComfyUI-AnimateDiff-Evolved, ComfyUI-Advanced-ControlNet节点报错解决
  • 网络安全域的划分与隔离
  • 基于RKNN的嵌入式深度学习开发(1)
  • 青蛙跳杯子(BFS)
  • 如何将hf-mirror.com作为vllm默认的下载源? conda如何移除虚拟环境?conda 如何复制一份虚拟环境?
  • 15-YOLOV8OBB损失函数详解
  • Spring(三)容器-注入
  • 玩转大模型——Trae AI IDE国内版使用教程
  • 【我的Android进阶之旅】如何使用NanoHttpd在Android端快速部署一个HTTP服务器?
  • GPU、NPU与LPU:大语言模型(LLM)硬件加速器全面对比分析
  • 20241130 RocketMQ本机安装与SpringBoot整合
  • CSS2.1基础学习
  • STM32——DMA详解
  • 做毕业设计哪个网站好/品牌推广专员
  • 如何对网站做压力测试/长沙网站关键词推广
  • 重庆政府网站建设方案/百度网站怎么提升排名
  • 怎样进入网站的后台/西安seo服务培训
  • 中国邮政做特产的网站/百度置顶广告多少钱
  • 网站动态logo怎么做/前端培训费用大概多少