【手撕OJ题】——141/142. 环形链表

news2024/9/20 17:34:14

目录

  • 🕒 题目 Ⅰ
  • ⌛ 方法① - 快慢指针
  • 🕒 面试题
  • 🕒 题目 Ⅱ
  • ⌛ 方法① - 快慢指针
  • ⌛ 方法② - 转换为链表相交问题

🕒 题目 Ⅰ

🔎 141. 环形链表【难度:简单🟢】

给你一个链表的头节点 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) 内存解决此问题吗?

⌛ 方法① - 快慢指针

💡 思路:定义两个指针 fastslowslow指针每次移动一步,而fast指针每次移动两步。如果链表带环,二者最终会在链表的某个节点相遇。

代码实现如下:

bool hasCycle(struct ListNode *head) {
    struct ListNode* fast = head, *slow = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if(fast == slow)  //如果快指针与慢指针相遇就代表有环
            return true;
    }
    return false;  //如果循环可以结束就代表没环
}

🕒 面试题

1、为什么快指针每次走两步,慢指针每次走一步,二者最终一定会在环中相遇?

解答:假设slow进环之后,fast和slow之间的差距为N(即追赶距离)。每一次追逐的过程中,距离都会缩小1,过程为:N、N-1、N-2、N-3、…、1、0,距离为0的时候就是相遇

2、快指针一次走三步,慢指针一次走一步,可不可以?

解答:不一定,还是假设进环之后fast和slow之间的差距为N,每追逐一次,之间的距离缩小2步,过程为:N、N-2、N-4、N-6、…,对于这样的结果,如果之间的差距为偶数,最终之间的距离会变为0,说明追上了;如果之间的差距为奇数,最终之间的距离会变成1。然后继续追逐下去,差距就变成了-1,意味着fast和slow之间的距离变成了C-1,C为环的长度,然后重新追逐。
如果C-1是偶数,再追一圈就可以追上,如果C-1是奇数,永远追不上,一直进入新的追逐。

3、快指针一次走X步,慢指针一次走Y步可不可以?
解答:快指针一次走X步,慢指针一次走Y步,慢指针入环时二者的距离为N,快指针与慢指针之间的距离一次缩小X-Y,如果X-Y为1,则一定能够相遇;如果X-Y大于1,如2,3,4… … 可能相遇,也可能不相遇。

🕒 题目 Ⅱ

🔎 142. 环形链表Ⅱ【难度:中等🟡】

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

示例同上题,输出是返回索引为 pos值 的链表节点。

⌛ 方法① - 快慢指针

💡 思路:定义两个指针 fastslowslow指针每次移动一步,而fast指针每次移动两步。首先求出二者在环中的相遇点;然后一个指针从链表的开始走,另一个指针从相遇点开始走,最终二者会在入环点相遇。

假设环的长度为C,环之前的节点的长度为L,slow与fast在环中X距离处相遇,相遇时快指针已经在环中走了N圈,则如下图所示:

在这里插入图片描述

  • slow走的距离: L + X L+X L+X
  • fast走的距离: L + X + N × C L+X+N×C L+X+N×C
  • fast走的距离是slow的两倍: 2 × ( L + X ) = L + X + N × C 2×(L+X)=L+X+N×C 2×L+X=L+X+N×C
    化简得: L + X = N × C L+X=N×C L+X=N×C
    转化得: L = N × C − X = = > L = ( N − 1 ) × C + C − X L=N×C-X ==>L=(N-1)×C +C-X L=N×CX==>L=N1)×C+CX
  • 所以一个指针从相遇点开始走,另一个指针从头开始走,二者最终会在入环点相遇。

代码实现如下:

struct ListNode *detectCycle(struct ListNode *head) {
    struct ListNode* fast = head, *slow = head, *cur = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;

        // 找到相遇的节点
        if(fast == slow)
        {
            struct ListNode* meet = slow;
            // 一个指针从头开始走,另一个指针从相遇点开始走
            while(cur != meet)
            {
                cur = cur->next;
                meet = meet->next;
            }
            return cur;  // 二者最终会在入环点相遇
        }  
    }
    return NULL;  // 无环
}

⌛ 方法② - 转换为链表相交问题

💡 思路:定义两个指针 fastslowslow指针每次移动一步,而fast指针每次移动两步。首先求出二者在环中的相遇点,然后记录下相遇点的下一个节点并让相遇点的next指向NULL,让相遇点的下一个节点作为新链表的,与原链表求交点,最后再把相遇点的next复原。
在这里插入图片描述

注意:

  • 我们需要将相遇点的next置空,否则在求两个链表的交点的时候会发生死循环;
  • 由于题目要求不修改原链表,所以最后我们需要把相遇点的next复原;

🔎 相交链表回顾

代码实现如下:

// 求两个链表的交点
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    // 如果任意一个链表为空,则没有交点
    if (headA == NULL || headB == NULL) {
        return NULL;
    }

    struct ListNode *curA = headA;
    struct ListNode *curB = headB;

    // 当两个指针不同步时,继续循环
    while (curA != curB) {
        // 如果curA到达链表A的末尾,则重置到链表B的头部
        curA = (curA == NULL) ? headB : curA->next;
        // 如果curB到达链表B的末尾,则重置到链表A的头部
        curB = (curB == NULL) ? headA : curB->next;
    }

    // curA和curB要么同时为空(没有交点),要么同时指向交点
    return curA;
}

struct ListNode *detectCycle(struct ListNode *head) {
    struct ListNode* slow = head, *fast = head, *cur = head;
    while(fast && fast->next)
    {
        // 迭代
        slow = slow->next;
        fast = fast->next->next;

        // 找到相遇点
        if(slow == fast)
        {
            // 记录相遇点的下一个节点,并把链表从相遇点断开,避免求相交的时候发生死循环
            struct ListNode* meet = fast;
            struct ListNode* next = meet->next;
            meet->next = NULL;

            // 求两个链表相交
            struct ListNode* intersection = getIntersectionNode(cur, next);
            return intersection;

            // 恢复原链表
            meet->next = next;
        }
    }
    return NULL;  // 无环
}

❗ 转载请注明出处
作者:HinsCoder
博客链接:🔎 作者博客主页

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2069453.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Electron】桌面应用开发electron-builder打包报错问题处理

Electron 桌面应用开发electron-builder打包过程中各种报错问题处理 前一篇有写过 Electron 桌面应用开发快速入门到打包Windows应用程序 在安装到打包的整个过程中&#xff0c;我们都会遇到很多诡异的问题&#xff0c;接下来我将介绍我遇到的几个问题的解决方案 一、拉包的时…

普元EOS-微前端实现路由

1 前言 EOS微前端已经对路由进行了封装&#xff0c;内置了 $router 对象&#xff0c;直接使用就实现了路由。 2 实现代码 this.$router.push({ path: /a/b }) 由于这些就是普通的路由对象的封装&#xff0c;就不过多描述使用方法。 有兴趣的同学可自行百度了解 vue router对…

CAAC执照:无人机飞手培训就业组装技术很重要

一、CAAC执照概述 CAAC&#xff08;中国民用航空局&#xff09;颁发的无人机执照&#xff0c;全称为《民用无人机操控员执照》&#xff0c;是无人机行业中最权威和含金量最高的证书。考取该执照后&#xff0c;无人机飞手可以合法地申请空域、航线&#xff0c;并从事无人机相关…

【动态规划】简单多状态 dp 问题

简单多状态 dp 问题 1.面试题 17.16. 按摩师2.打家劫舍 II3.删除并获得点数4.粉刷房子4.买卖股票的最佳时机含冷冻期5.买卖股票的最佳时机含手续费6.买卖股票的最佳时机 III7.买卖股票的最佳时机 IV 点赞&#x1f44d;&#x1f44d;收藏&#x1f31f;&#x1f31f;关注&#x1…

前端技术(四)—— 最经典Node.JS全套教程

一、node简介 1. 浏览器中的 JavaScript 的组成部分 2. 思考&#xff1a;为什么 JavaScript 可以在浏览器中被执行 3.思考&#xff1a;为什么 JavaScript 可以操作 DOM 和 BOM 4. 浏览器中的 JavaScript 运行环境 5. 思考&#xff1a;JavaScript 能否做后端开发 6. Node.js介绍…

MySQL 的半同步模式

目录 1 半同步简介: 解决主从数据一致性问题 2 实现半同步模式实践操作 2.1 MASTER 2.2 SLAVE 1 2.3 SLAVE 2 2.4 查看client链接状态 2.5 SLAVE 服务器故障模拟 2.5.1 停止 SLAVE 的 IO_THREAD 2.5.2 查看SLAVE 的IO线程是否关闭 2.5.3 查看 MASTER 上 client 的连接状态…

专业视频编辑和制作软件Adobe Media Encoder(ME)win/mac下载安装和软件介绍

一、软件概述 1.1 软件简介 Adobe Media Encoder&#xff08;ME&#xff09;是由Adobe公司开发的一款专业视频编辑和制作软件&#xff0c;全称为Media Encoder&#xff0c;是Creative Cloud套件中的一个重要组件。Adobe ME以其强大的视频编码、转码、调整、剪辑、合成等功能&…

解决git checkout -b 拉取远端某分支到本地时报错

问题描述 日常开发场景中&#xff0c;经常会出现切分支的情况&#xff0c;所以git checkout 命令是非常高频的 git checkout -b feature/xxx默认情况下&#xff0c;这条命令是基于当前所在分支来开辟新分支feature/xxx 但是&#xff0c;还有一些情况&#xff0c;我们需要基于…

比Maven快2~10倍的编译工具mvnd简介与实战

概述 maven-mvnd&#xff0c;可简称&#xff08;或缩写&#xff09;mvnd&#xff0c;the Maven Daemon。Apache Maven团队借鉴Gradle和Takari后开发的更快的构建工具。mvnd内嵌Maven&#xff0c;开发者可无缝从Maven迁移到mvnd。 参考资料&#xff1a;GitHub。 mvnd中会启动…

C++ | Leetcode C++题解之第368题最大整除子集

题目&#xff1a; 题解&#xff1a; class Solution { public:vector<int> largestDivisibleSubset(vector<int>& nums) {int len nums.size();sort(nums.begin(), nums.end());// 第 1 步&#xff1a;动态规划找出最大子集的个数、最大子集中的最大整数vect…

『基础』线性代数-1行列式

行列式是什么-运算规则 排列&#xff1a;不同的 n 元排列共有 n! 个 逆序&#xff1a;小数排在大数后面&#xff0c;叫逆序&#xff1b;一个排列中逆序的总和叫做这个排列的逆序数&#xff0c;记为 τ ( j 1 , . . . , j n ) \tau(j_1,...,j_n) τ(j1​,...,jn​) 逆序数的计…

MySQL笔记01: MySQL入门_1.2 MySQL下载安装与配置

2.2 MySQL下载安装与配置 2.2.1 MySQL下载 MySQL中文官网&#xff1a;https://www.mysql.com/cn/ MySQL英文官网&#xff1a;https://www.mysql.com/ MySQL官网下载地址&#xff1a;https://www.mysql.com/downloads/ &#xff08;1&#xff09;点击“MySQL Community (GPL) Do…

WPF—画刷(使用画刷实现背景颜色渐变效果)

WPF—画刷(使用画刷实现背景颜色渐变效果) 在WPF中我们可以使用画刷来对我们的页面做出各种炫丽的效果&#xff0c;列如渐变&#xff0c;渲染等&#xff0c; 下列就为大家分享2个渐变效果示例&#xff1a; 实例1(由内到外) <Grid><!--背景颜色渐变 画刷--><G…

【项目】微服务及时通讯系统:编写核心类

文章目录 前言1. 核心数据结构1.1 用户信息1.2 会话信息1.3 消息信息 2. 建立目录3. 编写代码3.1 用户信息3.2 会话信息3.3 消息信息3.4 工具函数 4. data.h 完整代码总结 前言 在构建现代微服务架构的即时通讯系统时&#xff0c;核心数据结构的设计是至关重要的。它们不仅决定…

聊一聊AI能够应用在哪些地方

AI大模型的应用场景极为广泛&#xff0c;涵盖了众多行业和领域&#xff0c;以下是一些主要的应用场景&#xff1a; 1. 自然语言处理&#xff08;NLP&#xff09; 对话系统&#xff1a;如智能客服、虚拟助手等&#xff0c;通过自然语言与用户进行交互&#xff0c;提供信息查询…

探索AI大模型量化前沿技术:引领智能计算新潮流

大型语言模型&#xff08;LLMs&#xff09;通常因为体积过大而无法在消费级硬件上运行。这些模型可能包含数十亿个参数&#xff0c;通常需要配备大量显存的GPU来加速推理过程。 因此越来越多的研究致力于通过改进训练、使用适配器等方法来缩小这些模型的体积。在这一领域中&am…

【CAN总线测试】——通讯相关诊断测试

从0开始学习CANoe使用 从0开始学习车载测试 相信时间的力量 星光不负赶路者&#xff0c;时光不负有心人。 目录 1.节点超时故障 2.Busoff故障码测试 3.Busoff 状态下超时故障监测测试 4.欠压故障测试 5.过压故障测试 1.节点超时故障 用例编号 TG4_TC1 测试目的 检测D…

从零开始搭建 LVS 高可用集群 (单机)

从零开始搭建 LVS 高可用集群 (单机) 背景 从零开始搭建 LVS 高性能集群 (DR模式) 从零开始搭建 KeepalaivedLvs 高可用集群 &#xff08;Aliyun部署&#xff09; 经过前面2篇关于lvs集群部署文章&#xff0c;相信跟着部署文档&#xff0c;实际部署过集群的大家对lvs服务有了…

【html+css 绚丽Loading】000015 九转轮回珠

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享htmlcss 绚丽Loading&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495…

Total Uninstall - 专业 Windows 卸载清理工具,让软件卸载更彻底

Total Uninstall 是一款专业的卸载清理工具&#xff0c;可帮助我们无残留卸载各类软件。 这款工具相当强大&#xff0c;卸载能做到彻底清除软件痕迹&#xff0c;同时具备软件分析、安装记录、系统清理、软件备份搬家等多种功能。 软件支持买前免费试用&#xff0c;感兴趣的朋友…