环形链表之快慢指针

2023-11-08

前言

对于环形链表,通过快慢指针,如果存在环,这这两个指针一定会相遇,这是一种经典的判断环或是应用于环问题的思想。

一、案例

1、环形链表

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

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

如果链表中存在环 ,则返回 true 。 否则,返回 false 。
在这里插入图片描述

注:以O(1)内存进阶。

2、环形链表II

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

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

注:不允许修改 链表。

注:以O(1)内存进阶

二、题解

1、环形链表

1)可以直接遍历,把遍历的结点用Set记录下来,然后记录前查看set中是否有该节点。
2)对于O(1)内存进阶,
A)可以通过修改遍历过的节点的value为一个特殊值,然后一直遍历,如果中途碰到有value为特殊值,说明遍历过,即有环。
B)可每次遍历过之后,就修改前驱节点的next指向为相反的指向,不管是否有环,都不会在遍历中出现死循环,而是从左出即有环,从右出则无环。
C)快慢指针,可以通过快慢指针,就像在操场跑圈一样,速度快的能和速度慢能相遇。

2、环形链表II

1)从第一个的基础上,1)和2)的A)两种方式上都容易修改得来;而B)不适用;
2)对于C),可以得到最后相遇的地方,这个节点一定在环内,设为end节点。外层从头节点遍历到end节点,内层循环从end节点开始遍历,直到下一个end节点,看中途是否会碰到外层循环的中途节点。

3、源码

package com.xhu.offer.tencent;

import java.util.HashSet;
import java.util.Set;

//环形链表
public class HasCycle {
    public boolean hasCycle(ListNode head) {
        //一直next直到next == null 或者next到已经访问过的节点,分别返回true 和 false;
        Set<ListNode> mark = new HashSet<>();
        while (head != null) {
            if (mark.contains(head)) return true;
            mark.add(head);
            head = head.next;
        }
        return false;
    }

    //需要消耗O(1)内存来进阶
    public boolean hasCycle2(ListNode head) {
        //每向前走一步就改变链接方向,如果循环结束的最后一个节点是初始节点则有环。
        if (head == null || head.next == null) return false;
        ListNode point = head, pre1 = null, pre2;
        while (point.next != null) {
            //记录前向节点
            pre2 = point;
            //往后遍历
            point = point.next;
            //修改前向节点的next指向
            pre2.next = pre1;
            //跟新pre1
            pre1 = pre2;

        }
        return point == head;

    }

    //修改节点值
    public boolean hasCycle3(ListNode head) {
        while (head != null) {
            if (head.val == Integer.MAX_VALUE) return true;
            head.val = Integer.MAX_VALUE;
            head = head.next;
        }
        return false;
    }
    //快慢指针O(1)
    public boolean hasCycle4(ListNode head) {
        //快慢指针,如果有环,则一定会相遇。
        if (head == null || head.next == null) return false;
        ListNode point1 = head, point2 = head.next;
        while (point2 != null) {
            if (point1 == point2) return true;
            point1 = point1.next;
            point2 = point2.next;
            if (point2 == null) return false;
            point2 = point2.next;
        }
        return false;
    }

    //环形链表II
    public ListNode detectCycle(ListNode head) {
        //以环形链表I的基础,返回节点即可
        //一直next直到next == null 或者next到已经访问过的节点
        Set<ListNode> mark = new HashSet<>();
        while (head != null) {
            if (mark.contains(head)) return head;
            mark.add(head);
            head = head.next;
        }
        return null;
    }

    //以O(1)内存进阶
    public ListNode detectCycle2(ListNode head) {
        //以环形链表I的基础,返回节点即可
        //一直next直到next == null 或者next到已经访问过的节点
        Set<ListNode> mark = new HashSet<>();
        while (head != null) {
            if (mark.contains(head)) return head;
            mark.add(head);
            head = head.next;
        }
        return null;
    }

    //以O(1)内存进阶,在不修改链表的情况。
    public ListNode detectCycle3(ListNode head) {
        //快慢指针,如果有环,则一定会相遇。这个时候就能确定这个相遇的点一定在环内。
        //然后从头开始遍历到环内节点,寻找入环节点。
        if (head == null || head.next == null) return null;
        ListNode point1 = head, point2 = head.next,end = null;
        while (point2 != null) {
            if (point1 == point2) {
                end = point1;
                break;
            }
            point1 = point1.next;
            point2 = point2.next;
            if (point2 == null) return null;
            point2 = point2.next;
        }
        if(end == null) return null;
        while(head != end){
            point1 = end.next;
            while(point1 != end){
                if(point1 == head) return point1;
                point1 = point1.next;
            }
            head = head.next;
        }
        return end;
    }

    // Definition for singly-linked list.
    class ListNode {
        int val;
        ListNode next;

        ListNode(int x) {
            val = x;
            next = null;
        }
    }
}

4、寻找入环点的数学解法

/*
    target:找到入环点。
    快慢指针 找到相遇点,此时起始节点到达相遇点的距离是环长的N倍。
    设新的起始点在环点,则从此处出发,到达起始点的距离 等于 环点再走n圈一致 - k。
    而入环点未知,而起始点未知,根据两者关系,反推起始点。
     */
    public ListNode detectCycle(ListNode head) {
        // bug2:毕竟要先do,所以需要先判断链表是否为null.
        if (head == null) return null;
        // bug1:fast = head == null ? null : head.next;这样会导致其先走了一步,导致反推入环口时发生死循环。
        ListNode slow = head, fast = head;
        // 寻找相遇点。
        do {
            fast = fast.next;
            slow = slow.next;

            fast = fast == null ? null : fast.next;
        } while (slow != fast && fast != null);
        if (fast == null) return null;
        // 反推入环点。
        slow = head;
        while (slow != fast) {
            slow = slow.next;
            fast = fast.next;
        }
        return slow;
    }

    // Definition for singly-linked list.
    class ListNode {
        int val;
        ListNode next;

        ListNode(int x) {
            val = x;
            next = null;
        }
    }
}
// 非do while()
class DetectCycle2 {
    /*
    target:找到入环点。
    快慢指针 找到相遇点,此时起始节点到达相遇点的距离是环长的N倍。
    设新的起始点在环点,则从此处出发,到达起始点的距离 等于 环点再走n圈一致 - k。
    而入环点未知,而起始点未知,根据两者关系,反推起始点。
     */
    public ListNode detectCycle(ListNode head) {
        ListNode slow = head, fast = head == null ? null : head.next;
        // 寻找相遇点。
        while (slow != fast && fast != null) {
            fast = fast.next;
            slow = slow.next;

            fast = fast == null ? null : fast.next;
        }
        if (fast == null) return null;
        // 反推入环点。
        slow = head;
        fast = fast.next;// a + b + 1 = kN
        while (slow != fast) {
            slow = slow.next;
            fast = fast.next;
        }
        return slow;
    }

    // Definition for singly-linked list.
    class ListNode {
        int val;
        ListNode next;

        ListNode(int x) {
            val = x;
            next = null;
        }
    }
}

总结

1)还是注意观察问题的个性之处,思考其中的细节,而不是简单的算法应用,比如修改节点的值或是修改节点的next指针。算法思想和代码只是工具,重要的是特定问题中的抽象逻辑。
2)对于简单应用,就是熟练使用set。
3)掌握快慢指针这样的经典算法思想,碰到环就得触发快慢指针的记忆,这样才显得专业一点。

参考文献

[1]LeetCode 环形链表
[2]LeetCode 环形链表II

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

环形链表之快慢指针 的相关文章

  • 为什么 i++ 不是原子的?

    Why is i Java 中不是原子的 为了更深入地了解 Java 我尝试计算线程中循环的执行频率 所以我用了一个 private static int total 0 在主课中 我有两个线程 主题 1 打印System out prin
  • Play框架运行应用程序问题

    每当我尝试运行使用以下命令创建的新 Web 应用程序时 我都会收到以下错误Play http www playframework org Error occurred during initialization of VM Could no
  • 如何找到给定字符串的最长重复子串

    我是java新手 我被分配寻找字符串的最长子字符串 我在网上研究 似乎解决这个问题的好方法是实现后缀树 请告诉我如何做到这一点或者您是否有任何其他解决方案 请记住 这应该是在 Java 知识水平较低的情况下完成的 提前致谢 附 测试仪字符串
  • 在 HTTPResponse Android 中跟踪重定向

    我需要遵循 HTTPost 给我的重定向 当我发出 HTTP post 并尝试读取响应时 我得到重定向页面 html 我怎样才能解决这个问题 代码 public void parseDoc final HttpParams params n
  • 多个 Maven 配置文件激活多个 Spring 配置文件

    我想在 Maven 中构建一个环境 在其中我想根据哪些 Maven 配置文件处于活动状态来累积激活多个 spring 配置文件 目前我的 pom xml 的相关部分如下所示
  • 加速代码 - 3D 数组

    我正在尝试提高我编写的一些代码的速度 我想知道从 3d 整数数组访问数据的效率如何 我有一个数组 int cube new int 10 10 10 我用价值观填充其中 然后我访问这些值数千次 我想知道 由于理论上所有 3d 数组都存储在内
  • 控制Android的前置LED灯

    我试图在用户按下某个按钮时在前面的 LED 上实现 1 秒红色闪烁 但我很难找到有关如何访问和使用前置 LED 的文档 教程甚至代码示例 我的意思是位于 自拍 相机和触摸屏附近的 LED 我已经看到了使用手电筒和相机类 已弃用 的示例 但我
  • 反射找不到对象子类型

    我试图通过使用反射来获取包中的所有类 当我使用具体类的代码 本例中为 A 时 它可以工作并打印子类信息 B 扩展 A 因此它打印 B 信息 但是当我将它与对象类一起使用时 它不起作用 我该如何修复它 这段代码的工作原理 Reflection
  • Spring @RequestMapping 带有可选参数

    我的控制器在请求映射中存在可选参数的问题 请查看下面的控制器 GetMapping produces MediaType APPLICATION JSON VALUE public ResponseEntity
  • 无法解析插件 Java Spring

    我正在使用 IntelliJ IDEA 并且我尝试通过 maven 安装依赖项 但它给了我这些错误 Cannot resolve plugin org apache maven plugins maven clean plugin 3 0
  • 如何将 pfx 文件转换为 jks,然后通过使用 wsdl 生成的类来使用它来签署传出的肥皂请求

    我正在寻找一个代码示例 该示例演示如何使用 PFX 证书通过 SSL 访问安全 Web 服务 我有证书及其密码 我首先使用下面提到的命令创建一个 KeyStore 实例 keytool importkeystore destkeystore
  • JRE 系统库 [WebSphere v6.1 JRE](未绑定)

    将项目导入 Eclipse 后 我的构建路径中出现以下错误 JRE System Library WebSphere v6 1 JRE unbound 谁知道怎么修它 右键单击项目 特性 gt Java 构建路径 gt 图书馆 gt JRE
  • 如何在 javadoc 中使用“<”和“>”而不进行格式化?

    如果我写
  • Eclipse Java 远程调试器通过 VPN 速度极慢

    我有时被迫离开办公室工作 这意味着我需要通过 VPN 进入我的实验室 我注意到在这种情况下使用 Eclipse 进行远程调试速度非常慢 速度慢到调试器需要 5 7 分钟才能连接到远程 jvm 连接后 每次单步执行断点 行可能需要 20 30
  • Android 中麦克风的后台访问

    是否可以通过 Android 手机上的后台应用程序 服务 持续监控麦克风 我想做的一些想法 不断聆听背景中的声音信号 收到 有趣的 音频信号后 执行一些网络操作 如果前台应用程序需要的话 后台应用程序必须能够智能地放弃对麦克风的访问 除非可
  • 获取 JVM 上所有引导类的列表?

    有一种方法叫做findBootstrapClass对于一个类加载器 如果它是引导的 则返回一个类 有没有办法找到类已经加载了 您可以尝试首先通过例如获取引导类加载器呼叫 ClassLoader bootstrapLoader ClassLo
  • 在 Maven 依赖项中指定 jar 和 test-jar 类型

    我有一个名为 commons 的项目 其中包含运行时和测试的常见内容 在主项目中 我添加了公共资源的依赖项
  • 有没有办法为Java的字符集名称添加别名

    我收到一个异常 埋藏在第 3 方库中 消息如下 java io UnsupportedEncodingException BIG 5 我认为发生这种情况是因为 Java 没有定义这个名称java nio charset Charset Ch
  • java.lang.IllegalStateException:驱动程序可执行文件的路径必须由 webdriver.chrome.driver 系统属性设置 - Similiar 不回答

    尝试学习 Selenium 我打开了类似的问题 但似乎没有任何帮助 我的代码 package seleniumPractice import org openqa selenium WebDriver import org openqa s
  • 节拍匹配算法

    我最近开始尝试创建一个移动应用程序 iOS Android 它将自动击败比赛 http en wikipedia org wiki Beatmatching http en wikipedia org wiki Beatmatching 两

随机推荐

  • 微服务如何治理

    微服务远程调用可能有如下问题 注册中心宕机 服务提供者B有节点宕机 服务消费者A和注册中心之间的网络不通 服务提供者B和注册中心之间的网络不通 服务消费者A和服务提供者B之间的网络不通 服务提供者B有些节点性能变慢 服务提供者B短时间内出现
  • 每日学习07:Comparable接口的CompareTo的用法

    接口 Comparable 此接口强行对实现它的每个类的对象进行整体排序 这种排序被称为类的自然排序 类的 compareTo 方法被称为它的自然比较方法 字符串 数组列表 数组 所有可以 排序 的类都实现了java lang Compar
  • ThinkPHP5.1开发企业微信支付到零钱

    Wxpay php
  • npm启动vue应用开发服务器过程分析

    关于 npm run serve 命令启动vue应用开发环境的过程分析 1 npm run 命令执行时 npm run 命令执行时 会把 node modules bin目录添加到执行环境的PATH变量中 全局的没有安装的包 在node m
  • 本地IDEA中使用SonarQube扫描代码

    文章目录 背景 步骤 安装插件 配置 使用 背景 为了提高效率 在走代码CICD流程里的Sonarqube之前 先在本地提前进行一次扫描和修复 步骤 安装插件 2种方式 在IDE的插件管理中心安装名为 SonarQube Community
  • 爬虫高级应用(15. 基于Charles抓包软件抓取手机APP数据)

    目录 写在前面 配置安装Charles 安装Charles 下载相关证书 电脑证书 手机证书 设置代理 实操案例 抓取手机APP爱吾游戏宝盒数据 写在前面 移动App多使用异步的方式从服务端获取数据 抓取数据之前 要先分析移动App用于获取
  • 线性代数 --- 线性代数基本定理下(四个基本子空间他们两两正交,且互为正交补)

    正交子空间 前面我们已经知道了 两个向量的内积为0是勾股定理的另一种表现形式 现在我们来研究一下两个子空间之间的正交 虽然 我很不喜欢一上来就先给个定义 但我这里还是要给 sorry 现有两个子空间V和W 如果V中的任何一个向量v和W中的任
  • deepsort算法原理以及代码解析

    概述 前边我们讲了sort算法的原理 并且指出了它的不足 IDsw过大 为了解决该问题 17年时候sort算法的团队又提出了DeepSort算法 Deepsort在原来Sort算法的基础上 改进了以下内容 使用级联匹配算法 针对每一个检测器
  • .NET通用开发框架

    在开源中国社区 简单整理了下比较好的 NET通用开发框架 一个好的通用框架大概包括 开源 扩展性好 灵活性好 复用性好 维护性好 易测试 易发布 易部署 快速业务搭建 或业务集成 通用性强 参考资料多 持续技术支持 社区疑难问题建设 NET
  • 顺序表的基本操作(初始化、插入、删除、查询、扩容、打印、清空等)

    顺序表的基本操作 顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构 一般情况下采用数组存储 在数组上完成数据的增删查改等基本操作 初始化 初始化结构体 开辟空间 void SeqListInit SeqList ps size
  • TypeScript 中的 any、unknown、never 和 void

    any any 表示 任意类型 它是任意类型的父类 任意类型的值都可以赋予给 any 类型 编译不会报错 let anything any 前端西瓜哥 let flag boolean true anything flag anything
  • 数据库实体关系模型 --- ER Model

    数据库实体关系图 The Entity Relationship Model ER Model ER模型的作用 ER模型的基本组成 E R 图 ER图的基本组成 不同的键 Key 超码 superkey 候选码 candidate key
  • 微服务多模块:Springboot+Security+Redis+Gateway+OpenFeign+Nacos+JWT (附源码)仅需一招,520彻底拿捏你

    可能有些人会觉得这篇似曾相识 没错 这篇是由原文章进行二次开发的 前阵子有些事情 但最近看到评论区说原文章最后实现的是单模块的验证 由于过去太久也懒得验证 所以重新写了一个完整的可以跑得动的一个 OK 回到正题 以下是真正对应的微服务多模块
  • python习题及答案/4.16

    文章目录 1 从键盘输入两个数 求它们的和并输出 2 从键盘输入三个数到a b c中 按公式值输出 3 输出 Python语言简单易学 4 使用函数求特殊a串数列和 5 使用函数求素数和 6 使用函数统计指定数字的个数 1 从键盘输入两个数
  • 以太坊学习笔记(三)——搭建以太坊私链

    以太坊私链的搭建可以直接通过下载程序进行安装 也可以通过编译源码安装 本文介绍通过编译源码进行安装 编译源码 1 准备环境 我们下载的是go语言的源码 首先需要正确的安装go语言环境 如何正确安装go语言环境 大家可以去网上找教程 2 下载
  • AndroidO audio系统之AudioPolicyService分析(三)

    1 AudioPolicyService基础 AudioPolicy在Android系统中主要负责Audio 策略 相关的问题 它和AudioFlinger一起组成了Android Audio系统的两个服务 一个负责管理audio的 路由
  • QStringList 常用方法

    QStringList类 常用方法 定义一个字符串链表 QStringList weekList 往链表中添加元素 weekList lt lt 星期一 lt lt 星期二 lt lt 星期三 lt lt 星期四 weekList lt l
  • 麻雀算法SSA优化LSTM超参数

    前言 LSTM 航空乘客预测单步预测的两种情况 简单运用LSTM 模型进行预测分析 加入注意力机制的LSTM 对航空乘客预测采用了目前市面上比较流行的注意力机制 将两者进行结合预测 多层 LSTM 对航空乘客预测 简单运用多层的LSTM 模
  • Shapley Values

    今天来学习一下Shapley Values 先上概念 以及研究背景 borrowed from Wikipedia The Shapley value is a solution concept in cooperative game th
  • 环形链表之快慢指针

    环形链表 前言 一 案例 1 环形链表 2 环形链表II 二 题解 1 环形链表 2 环形链表II 3 源码 4 寻找入环点的数学解法 总结 参考文献 前言 对于环形链表 通过快慢指针 如果存在环 这这两个指针一定会相遇 这是一种经典的判断