[Data structure]单链表 | 一文介绍线性数据结构之一的单链表(Java实现)

news2024/11/27 18:38:40

⭐作者介绍:大二本科网络工程专业在读,持续学习Java,努力输出优质文章
⭐作者主页:@逐梦苍穹
⭐所属专栏:数据结构。数据结构专栏主要是在讲解原理的基础上拿Java实现
⭐如果觉得文章写的不错,欢迎点个关注一键三连😉有写的不好的地方也欢迎指正,一同进步😁

单链表

  • 1、简介
  • 2、实现单链表注意事项
  • 3、代码思路总览
  • 4、Node
  • 5、SingleList
    • 5.1、添加节点
      • 5.1.1、addNode
      • 5.1.2、addNodeByOrder
    • 5.2、删除节点
    • 5.3、修改节点
    • 5.4、显示链表内容

1、简介

在这里插入图片描述
  单链表(Singly linked list)是一种常见的线性数据结构,它由若干个节点组成,每个节点都包含数据元素和一个指向下一个节点的指针。单链表中的第一个节点称为头节点,最后一个节点称为尾节点,尾节点的指针指向NULL。
  单链表的优点在于,可以高效地进行节点的插入和删除操作,而不需要像数组那样频繁地移动数据元素。因此,单链表适用于频繁的插入和删除操作的场景。

下面是单链表的一些基本操作:

  1. 链表的创建:在创建链表时,需要首先创建头节点,并将其指针设置为NULL。然后逐个创建后续节点,并将前一个节点的指针指向当前节点,直到创建完整个链表。
  2. 链表的遍历:从头节点开始,逐个遍历链表中的节点,直到遍历到尾节点为止。
  3. 链表的插入:可以在链表的任意位置插入一个新节点。具体操作是先将新节点的指针指向其下一个节点,然后将前一个节点的指针指向新节点。
  4. 链表的删除:可以删除链表中的任意节点。具体操作是将待删除节点的前一个节点的指针指向待删除节点的下一个节点,然后释放待删除节点的内存空间。

单链表的时间复杂度如下:

  1. 链表的创建时间复杂度为O(n),其中n为链表中节点的个数。
  2. 链表的遍历时间复杂度为O(n),其中n为链表中节点的个数。
  3. 链表的插入时间复杂度为O(1)。
  4. 链表的删除时间复杂度为O(1)。

需要注意的是,单链表不支持随机访问,只能从头节点开始逐个遍历,因此其查找操作的时间复杂度为O(n),其中n为链表中节点的个数。此外,由于单链表中每个节点只包含一个指针,因此其空间复杂度较低,是一种比较节省空间的数据结构。

2、实现单链表注意事项

在实现单链表的过程中,需要注意以下几点:

  1. 头结点的处理:在单链表中,通常会定义一个头结点,头结点不存储数据,只是为了方便操作链表。因此,在实现链表的过程中,需要特别处理头结点的位置,以及它的next引用。
  2. 节点的添加和删除:在添加或删除节点时,需要特别注意节点的前后关系,以及边界条件的处理。比如,插入节点时,需要遍历找到最后一个节点,然后在它的next引用处插入新节点;而删除节点时,需要找到待删除节点的前一个节点,然后将它的next引用指向待删除节点的下一个节点。
  3. 链表长度的维护:在添加或删除节点时,需要维护链表的长度。可以定义一个size变量,记录链表的长度。
  4. 内存管理:在使用单链表时,需要注意内存的管理,避免出现内存泄漏等问题。在删除节点时,需要将待删除节点的内存释放掉。
  5. 并发安全:在多线程环境下使用单链表时,需要考虑并发安全的问题。可以使用线程安全的容器类,或者在访问链表时使用锁来保证并发安全。

综上所述,实现单链表时需要注意以上几点,以保证链表的正确性和高效性。

3、代码思路总览

因为链表的头节点约定不动,所以后面很多方法都会使用一个辅助节点temp来辅助实现相应的功能。
辅助节点一开始是直接指向链表头节点的,也就是说,辅助节点和头节点指向的是同一块内存空间。
⭐而且增删改查的过程,指针定位的不是目标节点,而是目标节点的前一个节点。因为单链表是单向的,只能访问下一个指针域的内存地址,如果指向目标节点,那么就无法改变上一个节点指向的地址,会损伤链表结构

  首先创建节点对象代码Node类,创建成员变量:唯一标识符id,节点的值value,节点Node类型的next(重点)以及重写toString方法。
  然后创建单链表类SingleList,先初始化头节点,然后依次编写各种方法:获取头节点、添加节点(两种方式)、删除节点、修改节点、显示链表内容。[总体实现思路如下图所示]:
在这里插入图片描述

4、Node

  重点说一下这里的重写toString方法。
  可以使用IDEA自动生成重写后的toString代码,但是重写后的toString,在显示Node对象的时候,next会把后面所有的节点内容都带上,不便于阅读。
  所以作出调整:如果是最后一个节点,则next的值为null;否则,直接打印next指向的地址。
在这里插入图片描述

this.getClass().getName() + "@" + Integer.toHexString(this.hashCode())来自Object类的toString源码:
在这里插入图片描述

下面是Node的代码:

package linkedList.singleList;

/**
 * @author 逐梦苍穹
 * @date 2023/4/27 13:55
 */
public class Node{
    public int id;
    private int value;
    private Node next;

    public Node(int id,int value){
        this.id = id;
        this.value = value;
    }

    @Override
    public String toString() {
        if (this.next==null){
            return "Node{" +
                    "id=" + this.id +
                    ", value=" + this.value +
                    ", next=" + null +
                    '}';
        }
        return "Node{" +
                "id=" + this.id +
                ", value=" + this.value +
                ", next=" + this.getClass().getName() + "@" + Integer.toHexString(this.hashCode()) +
                '}';
    }

    public Node getNext() {
        return next;
    }

    public void setNext(Node next) {
        this.next = next;
    }

    public int getValue() {
        return value;
    }

    public void setValue(int value) {
        this.value = value;
    }
}

5、SingleList

SingleList的全部代码如下,下面循序渐进详细讲解:

package linkedList.singleList;

/**
 * @author 逐梦苍穹
 * @date 2023/4/27 14:07
 */
public class SingleList {
    Node headNode = new Node(0, 0); //初始化头节点

    /**
     * 获取头节点
     */
    public Node getHead() {
        return headNode;
    }

    /**
     * 添加节点(直接按调用顺序添加)
     */
    public void addNode(Node node) {
        Node temp = this.headNode;
        while (true) {
            if (temp.getNext() == null) {
                break;
            } else {
                //辅助节点后移
                temp = temp.getNext();
            }
        }
        //跳出循环说明到达链表尾部,可以添加
        temp.setNext(node);//把最后一个节点的next指针置为下一个要添加的节点的地址值
    }

    /**
     * 根据id查找位置插入
     */
    public void addNodeByOrder(Node node) {
        Node temp = this.headNode;

        while (true) {
            if (temp.getNext() == null) {
                temp.setNext(node);
                break;
            }
            if (node.id < temp.getNext().id) {
                node.setNext(temp.getNext());
                temp.setNext(node);
                break;
            }
            temp = temp.getNext();
        }
    }

    /**
     * 删除节点
     */
    public void deleteNode(int id) {
        Node temp = this.headNode;
        if (temp.getNext() == null) {
            System.out.println("链表空无法删除");
            return;
        }
        while (true) {
            if (temp.getNext() == null) break;
            if (temp.getNext().id == id) {
                temp.setNext(temp.getNext().getNext());
                System.out.println("Node{id=" + id + "}:删除成功");
                return;
            }
            temp = temp.getNext();
        }
        System.out.println("无此节点");
    }

    /**
     * 修改节点
     */
    public void updateNode(int id, int newValue) {
        Node temp = this.headNode;
        if (temp.getNext() == null) {
            System.out.println("链表空");
        }
        while (true) {
            if (temp.getNext() == null) break;
            if (temp.getNext().id == id) {
                temp.getNext().setValue(newValue);
                System.out.println("Node{id=" + id + ", newValue=" + newValue + "}");
                return;
            }
            temp = temp.getNext();
        }
        System.out.println("无此节点");
    }

    /**
     * 打印整个链表内容
     */
    public void outLinkedList() {
        //方式一
        Node temp = this.headNode.getNext();
        if (temp == null) {
            System.out.println("链表空");
            return;
        }
        while (true) {
            System.out.println(temp);
            if (temp.getNext() == null) break;
            temp = temp.getNext();
        }
        //方式二
//        if (headNode.getNext() == null){
//            System.out.println("链表空");
//        }
//        Node temp = this.headNode.getNext();
//        while (true){
//            if (temp == null){
//                break;
//            }
//            System.out.println(temp);
//            temp = temp.getNext();
//        }
    }
}

5.1、添加节点

添加节点有两种方式:直接添加到链表尾部、按唯一标识id按顺序添加

5.1.1、addNode

在这里插入图片描述

直接添加到链表尾部:先判断当前节点的next域是不是null,如果是,说明到达链表尾部,可以添加;如果不是,则链表指针需要后移,直到抵达尾部:
在这里插入图片描述

5.1.2、addNodeByOrder

按唯一标识id按顺序添加:这里在while中有两个if判断,一开始是指向头节点的,这时候链表如果为空,则直接添加数据即可;当链表不为空的时候,进入的是第二个if分支,由于每一次插入的时候,都是保持有序的,所以在插入节点的时候只需要判断:想要插入节点的位置的下一个节点的id大于想要插入节点的id即可。
  在这里插入图片描述

插入的时候,比如要在节点A和C之间插入节点B。一开始A的next指向的是C,此时找到节点A,先把节点B的next置为C的地址,然后把A的next指向节点B的地址,即完成插入。
在这里插入图片描述

5.2、删除节点

删除节点的操作,同样需要一个辅助节点,指针同样需要指向目标节点的前一个节点。
被删除的节点,是不需要手动释放资源的,JVM有自动回收机制。
在这里插入图片描述
在这里插入图片描述

5.3、修改节点

在这里插入图片描述

5.4、显示链表内容

显示链表内容的思路,其实无非也就是遍历。这里采用的是while进行遍历,因为并不知道链表到底有多长,所以采用while遍历更合适。
遍历终止条件:链表为空 或者 到达最后一个节点(即在判断不为空的情况下出现next为null)
方式一:
  在这里插入图片描述

方式二:
  在这里插入图片描述

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

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

相关文章

R语言 | 数据框

目录 一、认识数据框 7.1 建立第一个数据框 7.2 验证与设定数据框的列名和行名 二、认识数据框的结构 三、获取数据框内容 3.1 一般获取 3.2 特殊字符$ 3.3 再看取得的数据 四、使用rbind()函数增加数据框的行数据 五、使用cbind()函数增加数据框的列数据 5.1 使用$符号…

《LearnUE——基础指南:开篇—3》——基础概念

目录 程序之祖——HellowWorld 0.3.1 创建HellowWorld工程 0.3.2编译类型 0.3.3 平台支持 0.3.4 命名约定 程序之祖——HellowWorld 0.3.1 创建HellowWorld工程 接上文准备工作完成之后&#xff0c;双击运行Unreal Engine快捷图标&#xff0c;或者双击生成的UE4Editor.exe…

【计算机图形学】图形变换(以任意直线为对称轴的对称变换)

模块3-2 图形变换 一 实验目的 编写图形各种变换的算法 二 实验内容 1&#xff1a;任意直线的对称变换。要求将变换矩阵写在实验报告中&#xff0c;并与代码匹配。求对任意直线AxByC0的对称变换矩阵。 实验结果如下图所示&#xff1a; 1&#xff1a;预设图形初始化 2&#…

数据结构与算法(小议递归二)

文章目录 前言一、例二二、为什么总结 前言 前面说到了递归在裴波那契数列计算中并不怎么适用&#xff0c;那么它适合什么样的场景呢&#xff1f; 我们继续举例和python3对比测试来说明。 一、例二 下面我们试试阶乘&#xff0c;在前面的代码上稍稍改一下就可以了&#xff1a…

ApachePOI操作Excel快速入门使用

简介 Apache POI 是一个处理Miscrosoft Office各种文件格式的开源项目&#xff0c;主要任务是创建和维护Java API&#xff0c;以基于Office Open XML标准&#xff08;OOXML&#xff09;和Microsoft的OLE 2复合文档格式&#xff08;OLE2&#xff09;处理各种文件格式&#xff0…

【Java笔试强训 3】

&#x1f389;&#x1f389;&#x1f389;点进来你就是我的人了博主主页&#xff1a;&#x1f648;&#x1f648;&#x1f648;戳一戳,欢迎大佬指点! 欢迎志同道合的朋友一起加油喔&#x1f93a;&#x1f93a;&#x1f93a; 目录 一、选择题 二、编程题 &#x1f525;最长的…

能用OpenCV做的15大计算机视觉任务

使用OpenCV&#xff0c;你几乎可以完成你能想到的每种计算机视觉任务。现实生活中的问题要求同时使用许多计算机视觉算法和模块来获得所需的结果。因此&#xff0c;你只需了解要用哪些OpenCV模块和函数来获得你想要的东西。 让我们来看看OpenCV中可以开箱即用的功能。 1 内置…

跨平台Office文档预览原生插件,非腾讯X5,支持离线,稳定高可用

引言 2023年4月13日零时起&#xff0c;腾讯浏览服务内核文档能力正式下线&#xff0c;要实现真正离线文档预览&#xff0c;于是有了这边文章。 前面写了多篇关于<跨平台文件在线预览解决方案>&#xff0c;不管使用pdf.js、LibreOffice&#xff0c;还是永中DCS&#xff…

JAVA设计模式之观察者模式( 通俗易懂的代码讲解 )

简述&#xff1a;Java的观察者模式是一种设计模式&#xff0c;它定义了一种对象间的一对多的依赖关系&#xff0c;使得当一个对象的状态发生改变时&#xff0c;所有依赖于它的对象都能够得到收到消息通知。在这种模式中&#xff0c;被观察者维护了一个观察者列表&#xff0c;并…

深入浅出 Compose Compiler(1) Kotlin Compiler KCP

前言 Compose 的语法简洁、代码效率非常高&#xff0c;这主要得益于 Compose Compiler 的一系列编译期魔法&#xff0c;帮开发者生成了很多样板代码。但编译期插桩也阻碍了我们对于 Compose 运行原理的认知&#xff0c;想要真正读懂 Compose 就必须先了解它的 Compiler。本系列…

前端系列11集-ES6 知识总结

ES Module 优点 静态分析 浏览器和 Node 都支持 浏览器的新 API 能用模块格式提供 不再需要对象作为命名空间 export 用于规定模块的对外接口 输出的接口与其对应的值是动态绑定关系可以取到模块内部实时的值 import 用于输入其他模块提供的功能 具有提升效果&#xff0c;会提升…

刷题4.28

1、 开闭原则软件实体&#xff08;模块&#xff0c;类&#xff0c;方法等&#xff09;应该对扩展开放&#xff0c;对修改关闭&#xff0c;即在设计一个软件系统模块&#xff08;类&#xff0c;方法&#xff09;的时候&#xff0c;应该可以在不修改原有的模块&#xff08;修改关…

服务注册与发现-Consul(Linux)

本文要有docker支持&#xff0c;docker的安装使用Docker 在Linux-CentOS上的安装使用_XiaoGuaiSs的博客-CSDN博客如果提示 [Warning] IPv4 forwarding is disabled. Networking will not work.然后将项目拷贝至linux 的project目录下&#xff08;随意&#xff09;。生成项目镜像…

Node第三方包 【Request】

文章目录 &#x1f31f;前言&#x1f31f;Request&#x1f31f;安装与使用&#x1f31f;流&#xff08;stream&#xff09;操作&#x1f31f;Form表单&#x1f31f;application/x-www-form-urlencoded (URL编码的Form)&#x1f31f;multipart/form-data (Multipart Form 上传) …

百度地图采集经纬度坐标数据定位的javascript实战开发(地理坐标拾取系统、地址定位点选插件、实时定位、数据导入、地理编码、位置纠偏)

坐标采集 前言1.百度地图地理坐标拾取系统2.位置选择插件百度地图经纬度选择插件默认参数配置 3.数据导入4.地理编码爬取百度webAPI返回参数前端封装转换函数 5.手机GPS定位GPS定位 6.位置纠偏html容器经纬度纠偏 前言 在百度地图的标注开发中&#xff0c;最为关键的操作就是经…

华硕主板来电开机自启

重启电脑&#xff0c;开机按del或者F2进入bios 按F10保存确认即可

浏览器跨站点通信(两个IP不同网站通信)

需求场景&#xff1a;OA系统会通过接口调用的方式将ERP系统的待办信息获取并显示在OA系统中。登录OA系统后&#xff0c;在OA系统中点击ERP系统的待办&#xff0c;会自动打开ERP系统业务处理页面&#xff0c;当ERP系统对应业务处理完毕&#xff0c;需要在OA系统中刷新待办记录&a…

【环境配置】解决No module named ‘librosa‘

执行以下命令下载 pip install librosa我这里遇到了报错&#xff1a; Microsoft Visual C 14.0 or greater is required. Get it with “Microsoft C Build Tools”: https://visualstudio.microsoft.com/visual-cpp-build-tools/ 相关解决方案请参考&#xff1a; 【环境配置…

Python渗透测试编程基础——线程、进程与协程

目录 一、进程与线程的概念 1.进程 2.线程 3.进程和线程的关系 4.任务执行方式 二、Python中的Threading模块 1.线程模块介绍 2.Threading介绍 &#xff08;1&#xff09;方法和属性 &#xff08;2&#xff09;类方法 三、线程简单编写 1.流程 2.创建线程 &#x…

云原生技术在云计算中的应用探讨

第一章&#xff1a;云原生技术的概念与发展 云原生技术是一种针对云计算环境设计的应用程序开发和部署方法&#xff0c;主要目标是提高应用程序的可伸缩性、可移植性、高可用性和自动化管理等方面的特性。这种技术是近年来在云计算领域兴起的一个新的开发模式&#xff0c;它主要…