数据结构学习--环形链表

环形链表

我们在判断一个链表是否是环形的,即首尾相连,我们可以以使用快慢指针,如果快指针能再次追上慢指针,就说明该链表是环形的,这边可以举个操场跑步的例子,当操场是环形的,跑的快的,就可以对跑的慢的实现套圈.

判断环形链表

给你一个链表,如果其是环形链表,则返回 true 否则返回 false

存放到集合中

分析:通过集合里面查找来判断是否回到头指针,不建议

public boolean hasCycle(ListNode head) {
    Set<ListNode> set = new HashSet<>();
    while (head != null) {
        //如果重复出现说明有环
        if (set.contains(head))
            return true;
        //否则就把当前节点加入到集合中
        set.add(head);
        head = head.next;
    }
    return false;
}

快慢指针(快2慢1)

package LinkList;

public class roundList {
    public class Solution {
        class ListNode {
            int val;
            ListNode next;

            public ListNode(int val, ListNode next) {
                this.val = val;
                this.next = next;
            }
        }
        public boolean hasCycle(ListNode head) {
            if (head == null){
                return false;
            }
            ListNode fast = head;
            ListNode slow = head;
            while (fast != null && fast.next != null){
                fast = fast.next.next;
                slow = slow.next;
                if (fast == slow){
                    return true;
                }
            }
            return false;
        }
    }
}

先反转再比较

如果反转过后的头结点与原来的头结点相同,那么就是环形链表
反转原理:

  1. 先给头结点的下个结点赋值一个新结点 temp
  2. 然后把头结点的 next 指向一个新节点 newHead
  3. 然后把那个新节点 newHead 指向 头结点
  4. 然后再令头结点 为 temp (之前头结点的下一个结点)
    在上面的流程中,temp充当相当于完成原来的链表的下个位置的指针,而newHead相当于是一种前驱指针的感觉,让原来在后面的元素指向前面的元素
public ListNode reverseList(ListNode head) {
        //新链表
        ListNode newHead = null;
        while (head != null) {
            //先保存访问的节点的下一个节点,保存起来
            //留着下一步访问的
            ListNode temp = head.next;
            //每次访问的原链表节点都会成为新链表的头结点,
            //其实就是把新链表挂到访问的原链表节点的
            //后面就行了
            head.next = newHead;
            //更新新链表
            newHead = head;
            //重新赋值,继续访问
            head = temp;
        }
        //返回新链表
        return newHead;
    }

    public boolean hasCycle(ListNode head) {
        ListNode rev = reverseList(head);
        if (head != null && head.next != null && rev == head) {
            return true;
        }
        return false;
    }

快慢指针的一般化

我们可以知道快指针要追上慢指针 需要多跑至少“一圈”,即一个链表长度,假设为m,当然所需要的时间就是 = 需要的圈数 / (快指针的速度 - 慢指针的速度)

为什么说多跑至少一圈?不妨想,快指针的速度 - 慢指针的速度 为偶数,而链表长度为奇数,那么就会出现第一次快指针追慢指针时,它们位置没有重合,而是快指针直接超越慢指针的情况,那么 快指针需要再追一圈,此时的圈数 = 2 * m,就一定为偶数就能追上

所以 所需要的时间就是 = 需要的圈数 / (快指针的速度 - 慢指针的速度) 必须为整数,当速度差分之圈数不是整数时,就需要多跑几圈来实现整除(即快慢指针的位置重合)

当然上面假设的环可能是从链表某处开始形成的环,有可能不是整个链表是环形
注意:循环的条件是快指针当前的不为null,如果要一次前进 m 步(即 fast = fast.m next)(m 个 next),那么就要保证其前面 m - 1个 next都不为null,否则会抛出空指针异常

代码(快3慢1)
ublic boolean hasCycle(ListNode head) {
        if (head == null)
            return false;
        ListNode slow = head;
        ListNode fast = head;
        while (fast != null && fast.next != null && fast.next.next != null) {
            slow = slow.next;
            fast = fast.next.next.next;
            if (slow == fast)
                return true;
        }
        return false;
    }
代码(快5慢1)
 public boolean hasCycle(ListNode head) {
        if (head == null)
            return false;
        ListNode slow = head;
        ListNode fast = head;
        while (fast != null && fast.next != null && fast.next.next != null && fast.next.next.next != null && fast.next.next.next.next != null) {
            slow = slow.next;
            fast = fast.next.next.next.next.next;
            if (slow == fast)
                return true;
        }
        return false;
    }
代码(快3慢2)
 public boolean hasCycle(ListNode head) {
        if (head == null)
            return false;
        ListNode slow = head;
        ListNode fast = head;
        while (fast != null && fast.next != null && fast.next.next != null) {
            slow = slow.next.next;
            fast = fast.next.next.next;
            if (slow == fast)
                return true;
        }
        return false;
    }

环形链表II

给你给定一个链表的头节点 head ,返回链表开始入环的第一个节点(在哪里开始形成环的)。 如果链表无环,则返回 null

分析

在这里插入图片描述
注:来源于魏梦舒老师的《算法漫画》
主要抓住两点,第一次相遇两指针的关系,一个走的路程是另外一个两倍

代码

public class roundListPlus {
    class ListNode{
        int val;
        ListNode next;

        public ListNode(int val) {
            this.val = val;
            next = null;
        }
    }
    public class Solution {
        public ListNode detectCycle(ListNode head) {
            ListNode fast = head;
            ListNode slow = head;
            // 快指针每次移动2,慢指针每次移动1
            // 用循环来寻找第一次相遇的点
            while (fast != null && fast.next != null){
                fast = fast.next.next;
                slow = slow.next;
                if (fast == slow){
                    break;
                }
            }
            // 循环结束要么是该表非环形,要么是快慢指针相遇
            // 判断,如果非环形链表,直接返回 null
            if ( fast == null || fast.next == null){
                return null;
            }
            // 根据递推关系,此时把其中一个指针放回头结点,另一个指针在第一次相遇位置
            // 此时两个指针一次都只移动1,下一次相遇就是入环结点
            slow = head;
            while (fast != slow){
                fast = fast.next;
                slow = slow.next;
            }
            return slow;
        }
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Docker Compose 的安装和使用详解

Docker Compose 是 Docker 官方开源的容器编排(Orchestration)项目之一,用于快速部署分布式应用。本文将介绍 Docker Compose 的基本概念、安装流程及使用方法。 简介 Compose 项目是 Docker 官方的开源项目,负责实现对 Docker 容器集群的快速编排。从功能上看,Docker C…

交换基础配置--单臂路由

1、创建vlan 创建vlan10 创建vlan10和vlan20 创建vlan1到vlan9 vlan1可以不用创建&#xff0c;因为交换机的所有接口默认为vlan1 本实验只需要vlan10和vlan20&#xff0c;以上只是介绍创建vlan的方法。 查看创建的vlan&#xff1a; sw2同理。接着将需要划分vlan的接口划入…

【java】29:IO流

文件&#xff1a; 1 什么是文件&#xff1a; 文件&#xff0c;对我们并不陌生&#xff0c;文件是保存数据的地方&#xff0c;比如大家经常使用的word文档,txt文件&#xff0c;excel文件.….都是文件。它既可以保存一张图片&#xff0c;也可以保持视频&#xff0c;声音.. .2 文…

开源Windows12网页版HTML源码

源码介绍 开源Windows12网页版HTML源码&#xff0c;无需安装就能用的Win12网页版来了Windows12概念版&#xff08;PoweredbyPowerPoint&#xff09;后深受启发&#xff0c;于是通过使用HTML、CSS、js等技术做了这样一个模拟板的Windows12系统&#xff0c;并已发布至github进行…

jvm-接口调用排查

问题描述 线上碰到个问题&#xff0c;某个接口调用时间特别长&#xff0c;线上调用接口直接报gateway time out 分析处理 1、先关闭该功能 &#xff08;该功能是非核心功能&#xff09; 2、本地起服务连环境排查&#xff0c;发现本地正常。并且线上其他接口正常&#xff0c;…

回归预测 | Matlab实现SA-BP模拟退火算法优化BP神经网络多变量回归预测

回归预测 | Matlab实现SA-BP模拟退火算法优化BP神经网络多变量回归预测 目录 回归预测 | Matlab实现SA-BP模拟退火算法优化BP神经网络多变量回归预测预测效果基本描述程序设计参考资料 预测效果 基本描述 1.Matlab实现SA-BP模拟退火算法优化BP神经网络多变量回归预测&#xff0…

Ansible初识以及安装

1. Ansible应用简述&#xff1a; Ansible基于python语言实现&#xff0c;由Paramiko和PyYAML两个关键模块构建。具有独特的设计理念&#xff1a; 1&#xff09;安装部署简单 2&#xff09;管理主机便捷&#xff0c;支持多主机并行管理 3&#xff09;避免在被管理主机上安装客户…

【GitBlit】Windows搭建Git服务器详细教程

前言 如果公司或个人想在 Windows 环境下搭建私有的 Git 服务器&#xff0c;那么这个开源的 GitBlit 是一个不错的选择。 Gitblit 是一个开源纯 Java 的用于管理、查看和服务 Git 存储库。它是一个小型的托管集中式存储库工具。支持 SSH、HTTP 和 GIT 协议&#xff0c;开箱即…

通过PyCharm平台开发Django应用程序

学会使用命令行工具开发Django应用程序是基础&#xff0c;不过更多的时候还是要借助平台开发工具。目前&#xff0c;最好的Django应用程序开发工具就是jetBrains公司推出的PyCharm平台了。 借助PyCharm开发平台&#xff0c;可以极大提高开发Django应用程序的效率&#xff0c;同…

静态成员与友元,内部类初识

一.静态成员 1.静态成员变量 &#xff08;1&#xff09;定义&#xff1a; 类的定义中以静态声明的变量 &#xff08;2&#xff09;写法&#xff1a; static 变量声明 &#xff08;3&#xff09;注意&#xff1a; 静态成员变量不在对象中&#xff0c;在静态区&#xff0c…

从OWASP API Security TOP 10谈API安全

1.前言 应用程序编程接口&#xff08;API&#xff09;是当今应用驱动世界创新的一个基本元素。从银行、零售、运输到物联网、 自动驾驶汽车、智慧城市&#xff0c;API 是现代移动、SaaS 和 web 应用程序的重要组成部分&#xff0c;可以在面向客 户、面向合作伙伴和内部的应用程…

Xinstall:实现注册后自动绑定,提升用户体验

在移动互联网时代&#xff0c;App的注册与绑定流程对于用户体验至关重要。繁琐的注册步骤和手动绑定操作往往会让用户望而却步&#xff0c;导致用户流失。为了解决这一问题&#xff0c;Xinstall品牌推出了注册后自动绑定功能&#xff0c;极大提升了用户体验。 Xinstall的自动…

手写Java设计模式之抽象工厂模式,附源码解读

接上篇&#xff0c;抽象工厂模式将汽车的一些属性可以抽象出来&#xff0c;可以理解为给不同汽车品牌生成时加上不同的特性&#xff0c;如颜色等&#xff0c;具体代码如下&#xff1a; 引入颜色接口&#xff1a; public interface Colour {void fill(); }将颜色与汽车生成品牌…

线性代数基础2矩阵

矩阵是什么 矩阵就是二维数组&#xff0c;下面是一个 m 乘 n 的矩阵&#xff0c;它有 m 行&#xff0c;n 列&#xff0c;每行每列上面都有元素&#xff0c;每个元素都有行标i 和列标 j&#xff0c; a ij 。简称m n矩阵&#xff0c;记作&#xff1a; 注意a11的索引是 A[0,0]。…

Redis入门到通关之数据结构解析-RedisObject

文章目录 ☃️概述☃️源码 ☃️概述 RedisObject 是 Redis 中表示数据对象的结构体&#xff0c;它是 Redis 数据库中的基本数据类型的抽象。在 Redis 中&#xff0c;所有的数据都被存储为 RedisObject 类型的对象。 RedisObject 结构体定义如下&#xff08;简化版本&#xf…

网络流问题详解

1. 网络最大流 1.1 容量网络和网络最大流 1.1.1 容量网络 设 G(V, E)是一个有向网络&#xff0c;在 V 中指定了一个顶点&#xff0c;称为源点&#xff08;记为 Vs&#xff09;&#xff0c;以及另一个顶点&#xff0c;称为汇点&#xff08;记为 Vt&#xff09;&#xff1b;对…

【ds】替换空格

用‘%20’替换空格 var replaceBlank (charArr)> {if (!charArr || charArr.length 0) return var len charArr.lengthlet spaceLen 0for (let i 0; i < len; i) {if (charArr[i] ) {spaceLen}}var extraLen spaceLen * 2 // -> 20% 每一个空格需要增加2个ch…

大型网站系统架构演化实例_4.数据库读写分离

1.数据库读写分离 网站在使用缓存后&#xff0c;使对大部分数据读操作访问都可以不通过数据库就能完成&#xff0c;但是仍有一部分操作&#xff08;缓存访问不命中、缓存过期&#xff09;和全部的写操作都需要访问数据库&#xff0c;在网站的用户达到一定规模后&#x…

Python-VBA函数之旅-getattr函数

目录 一、getattr函数的常见应用场景&#xff1a; 二、getattr函数使用注意事项&#xff1a; 1、getattr函数&#xff1a; 1-1、Python&#xff1a; 1-2、VBA&#xff1a; 2、推荐阅读&#xff1a; 个人主页&#xff1a;https://blog.csdn.net/ygb_1024?spm1010.21…

大模型应用开发基础

AGI 时代&#xff0c;AI 无处不在&#xff0c;形成新的社会分层&#xff1a; AI 使用者&#xff0c;使用别人开发的 AI 产品AI 产品开发者&#xff0c;设计和开发 AI 产品基础模型相关&#xff0c;训练基础大模型&#xff0c;或为大模型提供基础设施 越向下层&#xff0c;重要…