IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> LeetCode每日一练 —— 环形链表问题(面试四连问) -> 正文阅读

[数据结构与算法]LeetCode每日一练 —— 环形链表问题(面试四连问)

🌟 前言

Wassup guys!我是Edison 😎

今天是 LeetCode 上的两道题: 141. 环形链表142. 环形链表 II

Let’s get it!

在这里插入图片描述



判断链表是否有环

1. 题目描述

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

示例 1:
在这里插入图片描述

示例 2:
在这里插入图片描述

示例 3:
在这里插入图片描述

2. 思路分析

这道题的思路很简单,还是用 快慢指针

每当慢指针 slow 前进一步,快指针 fast 就前进两步。
?
如果 fast 最终遇到空指针,说明链表中没有环;
?
如果 fast 最终和 slow 相遇,那肯定是 fast 超过了 slow 一圈,说明链表中含有环。

注意,如果链表没有环:

链表如果是 奇数 个,那么 slow 走一步,fast 走两步的话,那么 fast 一定走到 尾节点 ,所以终止循环的条件就是 fast->next != NULL
?
链表如果是 奇数 个,那么 slow 走一步,fast 走两步的话,那么 fast 一定走到 NULL 的位置,所以终止循环的条件就是 fast != NULL

3. 动图演示

准备两个指针 fastslow,循环链表;

slow 指针初始也指向 head,每次循环向前走一步;

fast 指针初始指向 head,每次循环向前两步;

如果没有环,则快指针会抵达终点,如果有环,那么快指针会追上慢指针(动图演示👇)
在这里插入图片描述

4. 代码实现

接口代码

bool hasCycle(struct ListNode *head) {
    // 快慢指针初始化指向 head
    struct ListNode* slow = head; 
    struct ListNode* fast = head; 

    // 快指针 走到末尾时停止
    while (fast && fast->next) {
        slow = slow->next; //慢指针走一步
        fast = fast->next->next; //快指针走两步
        // 快慢指针相遇,说明含有环
        if (slow == fast) {
            return true;
        }
    }
    // 不包含环
    return false;
}

提交结果
在这里插入图片描述

返回链表入环的第一个结点

1. 题目描述

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

示例 1:
在这里插入图片描述

示例 2:
在这里插入图片描述

示例 3:
在这里插入图片描述

2. 思路分析

这已经不是一道简单的代码题了,确切的来说,是 数学公式推导 + 逻辑结合 的一道题。
?
为什么要这样呢?这里简单说一下其中的原理。

通过上面的第一道题,已经学会了判断链表是否有环了,那么接下来要找这个环的入口了。

假设从 头结点 到环形 入口节点 的节点数为 x

环形 入口节点fast 指针与 slow 指针 相遇节点 节点数为 y

相遇节点 再到环形 入口节点 的节点数为 z。 (如图所示👇)
在这里插入图片描述

那么相遇时:

slow 指针走过的节点数为:: x + y x + y x+y

fast 指针走过的节点数: x + y + n ( y + z ) x + y + n (y + z) x+y+n(y+z)nfast 指针在环内走了 n 圈才遇到 slow 指针, ( y + z ) (y+z) y+z为一圈内节点的个数。

因为 fast 指针是一步走两个节点,slow 指针一步走一个节点, 所以 f a s t 指针走过的节点数 = s l o w 指针走过的节点数 ? 2 fast 指针走过的节点数 = slow 指针走过的节点数 * 2 fast指针走过的节点数=slow指针走过的节点数?2,也就是👇

( x + y ) ? 2 = x + y + n ( y + z ) (x + y) * 2 = x + y + n (y + z) (x+y)?2=x+y+n(y+z)

两边同时消掉一个 ( x + y ) (x+y) (x+y),化简得: x + y = n ( y + z ) x + y = n (y + z) x+y=n(y+z)

因为我们要找环形的入口,那么要求的是 x,因为 x 表示 头结点环形入口节点 的距离。

所以我们要求 x ,将 x 单独放在左边: x = n ( y + z ) ? y x = n (y + z) - y x=n(y+z)?y

在从 n ( y + z ) n(y+z) n(y+z) 中提出一个 ( y + z ) (y+z) (y+z) 来,整理公式之后为如下公式: x = ( n ? 1 ) ( y + z ) + z x = (n - 1) (y + z) + z x=(n?1)(y+z)+z

注意这里 n 一定是大于等于 1 的,因为 fast 指针 至少要多走一圈 才能相遇 slow 指针。

这个公式说明什么呢?

先拿 n1 的情况来举例,意味着 fast 指针在环形里转了一圈之后,就遇到了 slow 指针了。

n1 的时候,公式就化解为 x = z x = z x=z

这就意味着,从头结点出发一个指针,从相遇节点也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是环形入口的节点。

也就是在相遇节点处,定义一个指针 index1,在头结点处定一个指针 index2

index1index2 同时移动,每次移动一个节点, 那么他们相遇的地方就是环形入口的节点。 (动图演示👇)
在这里插入图片描述

那么 n 如果大于 1 是什么情况呢?就是 fast 指针在环形转 n 圈之后才遇到 slow 指针。

其实这种情况和 n1 的时候 效果是一样的,一样可以通过这个方法找到 环形的入口节点,只不过,index1 指针在环里 多转了 (n-1) 圈,然后再遇到 index2,相遇点依然是环形的入口节点。

3. 简化思路

我们假设 快慢指针相遇 时,慢指针 slow 走了 k 步,那么快指针 fast 一定走了 2k 步(如图所示👇)
在这里插入图片描述

fast 一定比 slow 多走了 k 步,这多走的 k 步其实就是 fast 指针在环里转圈圈,所以 k 的值就是环长度的「整数倍」。

假设 相遇点环的起点 的距离为 m,(见下图👇)那么 环的起点 距头结点 head 的距离为 k - m,也就是说如果从 head 前进 k - m 步就能到达环起点。

如果从相遇点继续前进 k - m 步,也恰好到达环起点。因为结合上图的 fast 指针,从相遇点开始走 k 步可以转回到相遇点,那走 k - m 步肯定就走到环起点了(如图所示👇)
在这里插入图片描述
所以,只要我们把快慢指针中的任一个重新指向 head,然后两个指针同速前进,k - m 步后一定会相遇,相遇之处就是环的起点了

4. 代码实现

接口代码

struct ListNode *detectCycle(struct ListNode *head) {
    struct ListNode* slow = head;
    struct ListNode* fast = head;

    while (fast && fast->next) {
        slow = slow->next; // 慢指针走一步
        fast = fast->next->next; // 快指针走两步
        // 相遇了
        if (slow == fast) {
            struct ListNode* meet = slow;
            while (meet != head) {
                meet = meet->next; // 一个指针从出发点开始走
                head = head->next; // 一个指针从出发点开始走  
            }
            // meet和head相等时,返回的就是入口点
            return meet;
        }
    }
    // 链表不带环
    return NULL;
}

提交结果
在这里插入图片描述

扩展追问

这道题的实现很简单,但是这里会有几个扩展问题,需要证明!

(1)slow 一次走 1 步,fast 一次走 2 步,一定能追上吗?
?
(2)slow 一次走 1 步,fast 一次走 3 步,能追上吗?fast4 步呢?或者 fastn 步呢?能追上吗?
?
针对上面的两个问题作出解答,并证明!

🍑 问题一

slow 一次走 1 步,fast 一次走 2 步,一定能追上吗?

假设链表带环,两个指针最后都会进入环,快指针先进环慢指针后进环

当慢指针刚进环时,可能就和快指针相遇了,假设 快指针慢指针 之间的距离为 N

在他们移动的过程中,快指针往前走两步,慢指针走一步,此时,两个指针每移动一次,之间的距离就缩小 1 步,直到缩小为 0,那么就意味着他们相遇了,所以不会出现每次刚好是套圈的情况,

因此,在慢指针走到一圈之前,快指针肯定是可以追上慢指针的,即相遇。
在这里插入图片描述

🍑 问题二

slow 一次走 1 步,fast 一次走 3 步,能追上吗?fast4 步呢?或者 fastn 步呢?能追上吗?

在他们前进的过程中,快指针 每次走 3 步,慢指针 每次走 1 步,此时 快指针 肯定 先进环慢指针后进环

假设 慢指针 进环时,快指针慢指针 之间的距离为 N,又因为 快指针 每次走 3 步,慢指针 每次走 1 步,所以每走一次,他们之间的距离就缩小 2

此时要分 种情况:

1、如果 N 为偶数,那么他们之间的距离最终会缩小 0,也就是相遇。

2、如果 N 为奇数,那么他们之间的会减到 1,然后减到 负1,减到 负1 也就意味着他们之间的距离又变成了 C - 1C 是环的周长),此时又分为 2 种情况;

2.1、若 C 为奇数,则 C - 1 为偶数,因为他们之间的距离一次缩小 2,所以他们还是会相遇;

2.2、若 C 为偶数,则 C - 1 为奇数,也就是说他们之间的距离还是奇数,那么他们永远都不会相遇。

在这里插入图片描述

总结:

当慢指针走一步,快指针走三步时。
?
慢指针进环时快指针 之间的距离为 奇数,并且环的周长恰好为 偶数,那么他们会一直在环里面打转转,永远不会相遇。

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-08-06 11:07:37  更:2022-08-06 11:08:53 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 22:36:44-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码