数据结构---判断链表是否有环

news2024/9/26 5:21:02

判断链表是否有环

  • 判断链表是否有环
  • 方法1
  • 方法2
    • JAVA实现
  • 问题扩展1
  • 问题扩展2

判断链表是否有环

有一个单向链表,链表中有可能出现“环”,就像下图这样。那么,如何用程序来判断该链表是否为有环链表呢?
在这里插入图片描述

方法1

创建一个以节点ID为Key的HashSet集合,用来存储曾经遍历过的节点。然后同样从头节点开始,依次遍历单链表中的每一个节点。每遍历一个新节点,都用新节点和HashSet集合中存储的节点进行比较,如果发现HashSet中存在与之相同的节点ID,则说明链表有环,如果HashSet中不存在与新节点相同的节点ID,就把这个新节点ID存入HashSet中,之后进入下一节点,继续重复刚才的操作。

遍历过5,3
在这里插入图片描述
遍历过过5、3、7、2、6、8、1
在这里插入图片描述
当再一次遍历节点2时,查找HashSet,发现节点已存在。

在这里插入图片描述
由此可知,链表有环。

时间复杂度是O(n)。
空间复杂度:O(n)。(使用了额外的存储空间)

方法2

利用指针
首先创建两个指针p1和p2(在Java里就是两个对象引用),让它们同时指向这
个链表的头节点。然后开始一个大循环,在循环体中,让指针p1每次向后移动1个节
点,让指针p2每次向后移动2个节点
,然后比较两个指针指向的节点是否相同。如果
相同,则可以判断出链表有环,如果不同,则继续下一次循环。

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
此方法就类似于一个追及问题

该算法的时间复杂度为O(n)。除两个指针外,没有使用任何额外的存储空间,所以空间复杂度是O(1)

JAVA实现

package algorithmProblem;
//判断链表是否有环




public class isCycle {

    /**
     * 链表节点
     */
    private static class Node{
        int data;
        Node next;

        public Node(int data) {
            this.data = data;
        }
    }

    /**
     * 判断是否有环
     * @param head 链表头节点
     * @return
     */
    public static boolean isCycle1(Node head){
        //建立俩个快慢指针
        Node p1 = head;//慢
        Node p2 = head;//快
        while (p2!=null&&p2.next!=null){
            p1 = p1.next;
            p2 = p2.next.next;
            if(p1==p2){
                //成功追及,有环
                return true;
            }
        }
        //没有环
        return false;
    }

    public static void main(String[] args) throws Exception{
        Node node1=  new Node(5);
        Node node2=  new Node(3);
        Node node3=  new Node(7);
        Node node4=  new Node(2);
        Node node5=  new Node(6);
        Node node6=  new Node(8);
        Node node7=  new Node(1);
        node1.next = node2;
        node2.next = node3;
        node3.next = node4;
        node4.next = node5;
        node5.next = node6;
        node6.next = node7;
        node7.next = node4;//这里就上环了
        System.out.println("链表是否有环: "+isCycle1(node1));

    }
}

问题扩展1

如果链表有环,如何求出环的长度?
在这里插入图片描述

环长 = 每一次速度差 × 前进次数 = 前进次数。

指针p1每次走1步,指针p2每次走2步,两者的速度差是1步。当两个指针再次相遇时,p2比p1多走了整整1圈。

package algorithmProblem;
//求环长
public class isCycle2 {

    /**
     * 链表节点
     */
    private static class Node{
        int data;
        isCycle2.Node next;

        public Node(int data) {
            this.data = data;
        }
    }

    /**
     * 判断是否有环
     * @param head 链表头节点
     * @return
     */
    public static boolean isCycle1(isCycle2.Node head){
        //建立俩个快慢指针
        isCycle2.Node p1 = head;//慢
        isCycle2.Node p2 = head;//快
        while (p2!=null&&p2.next!=null){
            p1 = p1.next;
            p2 = p2.next.next;
            if(p1==p2){
                //成功追及,有环
                return true;
            }
        }
        //没有环
        return false;
    }

    //在有环的情况下,求环长
    public static int lenCycle(isCycle2.Node head){
        //建立俩个快慢指针
        isCycle2.Node p1 = head;//慢
        isCycle2.Node p2 = head;//快
        //记录追及后的步数
        int pos=0;
        //记录追及的次数
        int time=0;
        //记录环的长度
        int len = 0;
        while (p2!=null&&p2.next!=null){
            p1 = p1.next;
            p2 = p2.next.next;
            if (p1==p2){
                System.out.println("追及");
                //第一次追上后把pos置零,开始计算追上后过了多少步,再次追上
                if(time==0){
                    pos=0;
                }
                time++;
                if(time==2){
                    break;
                }
            }
            pos++;

        }
        len = (2-1)*pos;
        return len;
    }

    public static void main(String[] args) throws Exception{
        isCycle2.Node node1=  new isCycle2.Node(5);
        isCycle2.Node node2=  new isCycle2.Node(3);
        isCycle2.Node node3=  new isCycle2.Node(7);
        isCycle2.Node node4=  new isCycle2.Node(2);
        isCycle2.Node node5=  new isCycle2.Node(6);
        isCycle2.Node node6=  new isCycle2.Node(8);
        isCycle2.Node node7=  new isCycle2.Node(1);
        node1.next = node2;
        node2.next = node3;
        node3.next = node4;
        node4.next = node5;
        node5.next = node6;
        node6.next = node7;
        node7.next = node4;//这里就上环了
        System.out.println("链表是否有环: "+isCycle1(node1));
        if(isCycle1(node1)){
            System.out.println("环长为: "+lenCycle(node1));
        }

        //System.out.println(node7.next.next.next.next.next.next.data);

    }
}

在这里插入图片描述

问题扩展2

如果链表有环,如何求出入环节点?
在这里插入图片描述

package algorithmProblem;
//求环长
public class isCycle2 {

    /**
     * 链表节点
     */
    private static class Node{
        int data;
        isCycle2.Node next;

        public Node(int data) {
            this.data = data;
        }
    }

    /**
     * 判断是否有环
     * @param head 链表头节点
     * @return
     */
    public static boolean isCycle1(isCycle2.Node head){
        //建立俩个快慢指针
        isCycle2.Node p1 = head;//慢
        isCycle2.Node p2 = head;//快
        while (p2!=null&&p2.next!=null){
            p1 = p1.next;
            p2 = p2.next.next;
            if(p1==p2){
                //成功追及,有环
                return true;
            }
        }
        //没有环
        return false;
    }

    //在有环的情况下,求环长
    public static int lenCycle(isCycle2.Node head){
        //建立俩个快慢指针
        isCycle2.Node p1 = head;//慢
        isCycle2.Node p2 = head;//快
        //记录追及后的步数
        int pos=0;
        //记录追及的次数
        int time=0;
        //记录环的长度
        int len = 0;
        while (p2!=null&&p2.next!=null){
            p1 = p1.next;
            p2 = p2.next.next;
            if (p1==p2){
                System.out.println("追及");
                if(time==0){
                    pos=0;
                }
                time++;
                if(time==2){
                    break;
                }
            }
            pos++;

        }
        len = (2-1)*pos;
        return len;
    }

    //求首次相遇点
    //在有环的情况下,求首次相遇点
    public static Node firstMeet(isCycle2.Node head){
        //建立俩个快慢指针
        isCycle2.Node p1 = head;//慢
        isCycle2.Node p2 = head;//快
        while (p2!=null&&p2.next!=null){
            p1 = p1.next;
            p2 = p2.next.next;
            if (p1==p2){
                System.out.println("首次相遇");
                return p1;
            }


        }
    return null;
    }

    /**
     * 求入环点
     * @param head      头指针
     * @param firstmeet 首次相遇点指针
     * @return
     */
    public static int intoPosition(isCycle2.Node head ,isCycle2.Node firstmeet){
        int len=0;
        while (head!=firstmeet){
            head=head.next;
            firstmeet =firstmeet.next;
            len++;

        }
        return len;
    }
    public static void main(String[] args) throws Exception{
        isCycle2.Node node1=  new isCycle2.Node(5);
        isCycle2.Node node2=  new isCycle2.Node(3);
        isCycle2.Node node3=  new isCycle2.Node(7);
        isCycle2.Node node4=  new isCycle2.Node(2);
        isCycle2.Node node5=  new isCycle2.Node(6);
        isCycle2.Node node6=  new isCycle2.Node(8);
        isCycle2.Node node7=  new isCycle2.Node(1);
        node1.next = node2;
        node2.next = node3;
        node3.next = node4;
        node4.next = node5;
        node5.next = node6;
        node6.next = node7;
        node7.next = node4;//这里就上环了
        System.out.println("链表是否有环: "+isCycle1(node1));
        if(isCycle1(node1)){
            System.out.println("环长为: "+lenCycle(node1));
        }
        if(isCycle1(node1)){
            System.out.println("首次相遇点距离头节点: "+intoPosition(node1,firstMeet(node1)));
        }


        //System.out.println(node7.next.next.next.next.next.next.data);

    }
}

在这里插入图片描述

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

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

相关文章

大数据Kudu(九):Spark操作Kudu

文章目录 Spark操作Kudu 一、​​​​​​​​​​​​​​添加Maven依赖

【DELM回归预测】基于灰狼算法改进深度学习极限学习机GWO-DELM实现数据回归预测附matlab代码

✅作者简介:热爱科研的Matlab仿真开发者,修心和技术同步精进,matlab项目合作可私信。 🍎个人主页:Matlab科研工作室 🍊个人信条:格物致知。 更多Matlab仿真内容点击👇 智能优化算法 …

网络请求与数据提取-urllib库

关于网络爬虫,其实就是模拟浏览器向网站服务器发送请求,然后从响应的结果中提取出需要的数据。那么,该如何实现这一流程了?对于初学者来说,可能都不知道该如何入手,学习爬虫时需不需要了解HTTP、TCP、IP 层…

入门:环境安装与部署

容器技术入门 随着时代的发展,Docker也逐渐走上了历史舞台,曾经我们想要安装一套环境,需要花费一下午甚至一整天来配置和安装各个部分(比如运行我们自己的SpringBoot应用程序,可能需要安装数据库、安装Redis、安装MQ等…

springboot常用语法库

今天与大家分享springboot常用语法库的基本语法。如果有问题,望大家指教。 目录 1. freemarker是什么 1.1 优点 2. springboot整合freemarker 2.1 pom.xml 2.2 项目配置文件 2.3 Controller 2.4 index.ftl 2.5 常用功能演示 1. freemarker是什么 FreeMarke…

OPENGL ES 2.0 知识串讲 (3)——SHADER的功能GLSL语法(I)

更多图形知识请关注我的公众号: 在第一节中,我们介绍过 OpenGL ES 与 GLSL 的主要功能,就是往绘制 buffer 上绘制图片。其中虽然 GLSL 制作的 shader 是穿插在 OpenGL ES 中使用,但是我们在流程中可以看出来,两大 shader(vertex shader 和 fragment shader)相对于 O…

大学毕业生就业信息管理平台

开发工具(eclipse/idea/vscode等): 数据库(sqlite/mysql/sqlserver等): 功能模块(请用文字描述,至少200字): 系统在功能设计充分利用信息化技术和互联网的优势,建立一个以浏览器为用户工作界面,实现跨 平台…

Hive电子商务消费行为分析项目

文章目录数据说明环境准备项目代码上传数据文件并创建数据表数据清洗数据可视化客户分析交易分析门店分析评价分析数据说明 某零售企业的门店最近一年收集的数据 customer_details.csv:客户信息 transaction_details.csv:交易信息 store_details.csv:门店信息 store_review.c…

第1章 基础知识简介

🌞欢迎来到C语言的世界 🌈博客主页:卿云阁 💌欢迎关注🎉点赞👍收藏⭐️留言📝 🌟本文由卿云阁原创! 🌠本阶段属于练气阶段,希望各位仙友顺利完成…

【机器码】原码、反码、补码的学习

目录 让我们看看这三个码是什么 原码、反码、补码各自的范围 补码的加减运算 根据自己学习做的笔记来记录一下 原码、反码、补码,巩固自己的学习成果。 有符号数是由机器数和真值组合而成 真值:数值数据的实际值,带有-符号 …

RL 实践(3)—— 悬崖漫步【QLearning Sarsa 各种变体】

本文介绍如何用 QLeaning 系列和 Sarsa 系列表格方法解经典的悬崖漫步 (Cliff Walking) 问题完整代码下载:4_[Gym Custom] Cliff Walking (Q-Learning series and Sarsa series) 文章目录1. 悬崖漫步环境 (Cliff Walking)2. 使用 TD 方法求解2.1 Sarsa2.1.1 Sarsa 原…

kali 安装AWVS [赠附件]

前言 1.AWVS简介 AWVS(Acunetix Web Vulnerability Scanner)是一款知名的网络漏洞扫描工具,通过网络爬虫测试网站安全,检测流行的Web应用攻击,如跨站脚本、sql 注入等。据统计,75% 的互联网攻击目标是基于…

项目中遇到的错误

项目中遇到的错误swagger2 和 swagger3swagger 文档的注解springboot 版本问题SQL 关键字异常Apifox 的使用集中版本管理swagger2 和 swagger3 swagger2和 swagger3 需要导入的依赖 <dependency><groupId>io.springfox</groupId><artifactId>springfo…

LabVIEW FPGA中可重入和非可重入子VI的区别

LabVIEW FPGA中可重入和非可重入子VI的区别 LabVIEW FPGAVI默认是可重入的。如果多次调用重入VI&#xff0c;则每个实例会占用FPGA器件的单独硬件资源。如果使用非重入VI&#xff0c;无论是并行多次调用还是仅调用一次&#xff0c;都只会创建一个硬件实例并将其用于该VI。 ​…

最常用的 9 个JavaScript 函数与示例

输出内容才能更好的理解输入的知识 前言&#x1f380; 如果你想深入图形、可视化等领域&#xff0c;那么肯定离不开 canvas、webgl、three.js 等一系列技术。在这众多技术中&#xff0c;选择canvas2d为基础来入门是一个不错的选择。 canvas在动画、可视化、图片处理等领域有着…

物联网通信技术原理 第5章

目录 5.1 移动通信的基本概念及发展历史 5.1.1 移动通信的基本概念 5.1.2 移动通信的发展历史&#xff08;理解&#xff09; 1.第一代移动通信系统(1G) 2.第二代移动通信系统(2G) 3.第三代移动通信系统(3G) 5.1.3 移动通信的发展趋势与展望 5.2 无线传播与移动信道 5.2…

哈希的应用:布隆过滤器(C++实现)

文章目录1. 布隆过滤器1.1 背景1.2 概念1.3 控制误判率2. 实现布隆过滤器2.1 布隆过滤器类2.2 Set2.3 Test2.4 删除3. 优点4. 缺陷4. 缺陷1. 布隆过滤器 1.1 背景 位图&#xff08;bitmap算法&#xff09;告诉我们&#xff0c;想判断一个元素是否存在于某个集合中&#xff0c…

c语言指针 字符 字符串

1、sizeof 某个类型或者某个变量在内存中占用字节数。 例如&#xff1a;sizeof(int) ; sizeof(i)&#xff1b;都可以使用 2、运算符& 获取变量的地址。 int i; scanf("%d",&i); 输入变量时&#xff0c;必须使用&运算符。 &操作符只能应…

机器学习100天(二):002 数据预处理之导入数据集

机器学习 100 天,今天讲的是:数据预处理之导入数据集。 首先,我们打开 spyder。新建一个 load_data.py 脚本。 第一步,导入标准库 机器学习常用的标准库有 3 个: 第一个:numpy,用于数据处理。 第二个:matplotlib.pyplot,用于画图。 第三个:pandas,用于数值分析…

python 爬虫

前言 一、什么是爬虫 爬虫&#xff1a;&#xff08;又称为网页蜘蛛&#xff0c;网络机器人&#xff0c;在 FOAF 社区中间&#xff0c;更经常地称为网页追逐者&#xff09;&#xff1b;它是一种按照一定的规则&#xff0c;自动地从互联网上抓取对于我们有价值的网络信息的程序…