Java数据结构篇——单链表的基本操作

1. 前言

在上一篇《Java数据结构篇——实现顺序表的增删查改》,我们已经熟悉了 ArrayList 的使用并且进行了简单的模拟实现。ArrayList底层使用数组来存储元素,由于其底层是一段连续的空间,当ArrayList 任意位置插入或者删除元素时,就需要将后序元素整体往前或者往后移动,时间复杂度为O(n),效率比较低,因此ArrayList 不适合做任意位置插入和删除比较多的场景。因此:Java集合这种又引入了 LinkedList,即链表结构。

2. 链表

链表是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中引用链接次序实现的

image-20231216144405243

注意:

  1. 从上图可看出,链表结构正在逻辑上是连续的,但是在物理上(内存)不一定连续。
  2. 现实中的节点一般都是从堆上申请出来的。
  3. 从堆上申请的空间,是按照一定的额策略来分配的,两次申请的空间可能连续,也可能不连续。

3. 单链表的实现

创建一个链表

public class MySingleList {

    // 节点
    static class ListNode {
        public int val; // 数值域 - 存放当前节点的值
        public ListNode next; // next域 指向下一个节点

        public ListNode(int val) {
            this.val = val;
        }
    }

    // 链表的属性 链表的头节点
    public ListNode head; // null
    
    public void createList() {
        ListNode node1 = new ListNode(1);
        ListNode node2 = new ListNode(2);
        ListNode node3 = new ListNode(3);
        ListNode node4 = new ListNode(4);

        node1.next = node2;
        node2.next = node3;
        node3.next = node4;

        this.head = node1;
    }
}

画图表示:

image-20231216155247814

3.1 打印链表

  1. 怎么从第一个节点走到第二个节点?

    答:head = head.next

  2. 什么时候算是把节点都遍历完成?

    答: head == null

代码实现:

	/***
     * 打印链表
     */
    @Override
    public void display() {
        ListNode cur = head;
        while (cur != null) {
            System.out.print(cur.val + " ");
            cur = cur.next;// 让cur这个节点 可以从一个节点走到下一个节点
        }
        System.out.println();
    }

3.2 头插法

在链表的第一个位置插入元素。

思路:

  1. 插入元素的next指向head
  2. head指向插入元素

image-20231216163615979

代码实现:

    /**
     * 头插法
     * @param data
     */
    @Override
    public void addFirst(int data) {
        ListNode node = new ListNode(data); // 定义一个节点
        node.next = head;
        head = node;
    }

3.3 尾插法

在链表的最后个位置插入元素

思路:

  1. 判断链表中是否有元素。
  2. 如果没有元素,直接添加头结点即可。
  3. 如果有元素,将原链表最后一个元素next指向插入的元素。

image-20231216165016370

代码实现:

	/**
     * 尾插法
     * @param data
     */
    @Override
    public void addLast(int data) {
        ListNode node = new ListNode(data); // 定义一个节点
        if (head == null) { // 链表一个元素都没有
            head = node;
        } else {
            ListNode cur = head;
            while (cur.next != null) {
                cur = cur.next;
            }
            cur.next = node;
        }
    }

3.4 任意位置插入元素

思路:

  1. 判断index是否合法(index < 0 或者 index 大于链表长度),如果不合法则抛出异常。
  2. 判断index 等于0或者index等于链表长度,则使用头插法或尾插法
  3. cur找到index - 1位置
  4. 插入元素的next指向curnext
  5. curnext指向插入的元素

image-20231216180417677

代码实现:

    /**
     * 在index位置 插入data
     * @param index
     * @param data
     */
    @Override
    public void addIndex(int index, int data) throws IndexException{
        if (index < 0 || index > size()) {
            throw new IndexException("index不合法:" + index);
        }
        ListNode node = new ListNode(data); // 定义一个节点
        if (head == null) {
            head = node;
            return;
        }
        if (index == 0) {
            addFirst(data);
            return;
        }
        if (index == size()) {
            addLast(data);
            return;
        }
        ListNode cur = searchPrevIndex(index);
        node.next = cur.next;
        cur.next = node;
    }

    /**
     * 找到index-1的位置
     * @param index
     * @return
     */
    private ListNode searchPrevIndex(int index) {
        ListNode cur = head;
        int count = 0;
        while (count != index - 1) {
            cur = cur.next;
            count++;
        }
        return cur;
    }

异常类:

public class IndexException extends RuntimeException{
    public IndexException() {

    }
    public IndexException(String msg) {
        super(msg);
    }
}

3.5 查找元素

代码实现:

    /***
     * 求当前链表 是否存在key
     * @param key
     * @return
     */
    @Override
    public boolean contains(int key) {
        ListNode cur = head;
        while (cur != null) {
            if (cur.val == key) {
                return true;
            }
            cur = cur.next;
        }
        return false;
    }

3.6 链表节点个数

代码实现:

	/**
     * 求当前链表 有多少个节点
     * @return
     */
    @Override
    public int size() {
        ListNode cur = head;
        int count = 0;
        while (cur != null) {
            count++;
            cur = cur.next;
        }
        return count;
    }

3.7 删除元素

思路:

  1. 判断链表是否为空,如果为空直接返回
  2. 判断删除元素是否为头节点,如果是则head指向headnext
  3. 定义指针找到要删除节点的前一个节点
  4. 前一个节点的next指向删除节点的next

image-20231216183736354

代码实现:

    /**
     *
      * @param key
     */
    @Override
    public void remove(int key) {
        if (head == null) {
            return;
        }
        if (head.val == key) {
            head = head.next;
            return;
        }
        ListNode cur = findPrevKey(key);
        if (cur == null) {
            return;// 链表里要没有删除的数字
        }
        ListNode del = cur.next;
        cur.next = del.next;
    }

    /**
     * 找到删除节点的前一个节点
     * @param key
     * @return
     */
    private ListNode findPrevKey(int key) {
        ListNode cur = head;
        while (cur.next != null) {
            if (cur.next.val == key) {
                return cur;
            } else {
                cur = cur.next;
            }
        }
        return null;
    }

3.8 [删除链表中指定的所有元素](203. 移除链表元素 - 力扣(LeetCode))

思路:

  1. 判断链表是否为空,如果是空直接返回
  2. 定义指针cur:可能要删除的节点
  3. 定义指针prev:可能要删除的节点的前驱
  4. 判断curval是不是要删除的元素,如果是prevnext指向curnextcur指向curnext;否则prev指向curcur指向curnext
  5. 判断头节点的val是否为的元素,如果是头节点指向头节点的neext

image-20231216210941437

代码实现:

    /**
     * 删除链表中所有的key
     * @param key
     */
    @Override
    public void removeAllKey(int key) {
        if (head == null) {
            return;
        }
        ListNode prev = head; // 表示当前可能要删除的节点
        ListNode cur = head.next; // 可能要删除节点的前驱

        while (cur != null) {
            if (cur.val == key) {
                prev.next = cur.next;
                cur = cur.next;
            } else {
                prev = cur;
                cur = cur.next;
            }
        }
        if (head.val == key) {
            head = head.next;
        }
    }

LeetCode运行结果:

image-20231216213052758

3.9 清空链表

当一个对象,没有被引用的时候,就会被回收掉

    /**
     * 清空链表
     */
    @Override
    public void clear() {
        head = null;
    }

[4. 代码](MySingleList/src · 白日依山璟/JavaSE_code - 码云 - 开源中国 (gitee.com))

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

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

相关文章

使用下载代替物理串口输出-STM32 Debug (printf) Viewer

使用下载代替物理串口输出-STM32 Debug 硬件要求配置方法代码要求打印输出结果 硬件要求 STM32的PB9、PB10引脚的串口1通常用作其他功能使用后&#xff0c;无法通过printf()函数打印输出想要调试输出查看变量或调试信息。现已使用另外一种方法实现printf()函数打印输出。 ST…

AutoGen多代理对话项目示例和工作流程分析

在这篇文章中&#xff0c;我将介绍AutoGen的多个代理的运行。这些代理将能够相互对话&#xff0c;协作评估股票价格&#xff0c;并使用AmCharts生成图表。 我们创建对话的目的是要求代理分析特定公司的股票价格&#xff0c;并制作股票价格图表。 为了实现这一目标&#xff0c;…

oracle DG 三种应用机制

首先理解不管是哪种机制&#xff0c;oracle都不是从主库直接传归档文件到备库&#xff0c;而是通过网络将主库的redo数据传输到备库&#xff1a; 1、普通DG是主库发生日志切换&#xff0c;备库把接收到的redo数据在备库通过归档进程生成为归档文件进行应用 2、ADG则是备库把接收…

Windows mysql5.7 执行查询/开启/测试binlog---简易记录

前言&#xff1a;基于虚拟机mysql版本为5.7&#xff0c;增量备份测试那就要用到binlog… 简述&#xff1a;二进制日志&#xff08;binnary log&#xff09;以事件形式记录了对MySQL数据库执行更改的所有操作。 binlog是记录所有数据库表结构变更&#xff08;例如CREATE、ALTER…

轻松搭建FPGA开发环境:第三课——Vivado 库编译与设置说明

工欲善其事必先利其器&#xff0c;很多人想从事FPGA的开发&#xff0c;但是不知道如何下手。既要装这个软件&#xff0c;又要装那个软件&#xff0c;还要编译仿真库&#xff0c;网上的教程一大堆&#xff0c;不知道到底应该听谁的。所以很多人还没开始就被繁琐的开发环境搭建吓…

在非联网、无网络环境下,fpm的安装和生成RPM包的使用案例

文章目录 前言1、安装fpm1.1、安装Ruby环境1.2、gem 安装 fpm 2、fpm使用2.1、fpm常用参数2.2、fpm使用案例2.2.1、fpmFirstDemo文件夹2.2.3、编写脚本文件2.2.4、生成RPM包2.2.5、RPM安装与卸载测试 前言 由于fpm采用Ruby语言开发&#xff0c;因此在使用之前需要先在您的虚拟…

Java只有值传递,没有引用传递!

结论&#xff1a;Java中的参数传递&#xff0c;只有值传递&#xff0c;没有引用传递&#xff01; 以下均为错误理解&#xff1a; 值传递和引用传递&#xff0c;区别在于传递的内容。如果是个值&#xff0c;就是值传递&#xff1b;如果是个引用&#xff0c;就是引用传递Java是引…

力扣日记12.13-【二叉树篇】从中序与后序遍历序列构造二叉树

力扣日记&#xff1a;【二叉树篇】从中序与后序遍历序列构造二叉树 日期&#xff1a;2023.12.13 参考&#xff1a;代码随想录、力扣 106. 从中序与后序遍历序列构造二叉树 题目描述 难度&#xff1a;中等 给定两个整数数组 inorder 和 postorder &#xff0c;其中 inorder 是二…

Prometheus

Prometheus [系统性能优化实践]JVM进阶实战之监控工具(Prometheus) https://www.cnblogs.com/johnnyzen/p/17388354.html ubuntu 22.04 配置 Prometheus 和 Grafana 服务器监控 https://blog.csdn.net/nvd11/article/details/128030197 Prometheus 是一个开源的监控系统&…

深入理解网络 I/O:单 Group 混杂模式|多 Group 主从模式

&#x1f52d; 嗨&#xff0c;您好 &#x1f44b; 我是 vnjohn&#xff0c;在互联网企业担任 Java 开发&#xff0c;CSDN 优质创作者 &#x1f4d6; 推荐专栏&#xff1a;Spring、MySQL、Nacos、Java&#xff0c;后续其他专栏会持续优化更新迭代 &#x1f332;文章所在专栏&…

Threejs漫天多彩粒子天空--粒子系统打造

一、导语 漫天多彩粒子天空特效应该也是Threejs项目中挺常见的一个需求&#xff0c;因为它是基于粒子系统&#xff0c;可以衍生出许多的不一样的方案&#xff0c;比如&#xff0c;星空特效&#xff0c;下雨特效&#xff0c;飘雪特效等等&#xff0c;不仅可以用在项目中增加氛围…

二叉搜索树的实现

本文旨在讲解如何编写一颗二叉搜索树&#xff0c;包括基本的增删查改的操作。 目录 一、二叉搜索树的概念 ​编辑二、二叉搜索树的编写 2.1节点的编写 2.2节点的插入 2.3节点的查找 2.4节点的删除 三、二叉搜索树的应用 四、 二叉搜索树的性能分析 五、完整代码 一、…

CD8+T细胞通过NKG2D-NKG2DL轴维持对MHC-I阴性肿瘤细胞的杀伤

今天给同学们分享一篇实验文章“CD8 T cells maintain killing of MHC-I-negative tumor cells through the NKG2D-NKG2DL axis”&#xff0c;这篇文章发表在Nat Cancer期刊上&#xff0c;影响因子为22.7。 结果解读&#xff1a; MHC-I阴性肿瘤的免疫疗法需要CD8 T细胞 作者先…

现代雷达车载应用——第2章 汽车雷达系统原理 2.2节 汽车雷达架构

经典著作&#xff0c;值得一读&#xff0c;英文原版下载链接【免费】ModernRadarforAutomotiveApplications资源-CSDN文库。 2.2 汽车雷达架构 从顶层来看&#xff0c;基本的汽车雷达由发射器&#xff0c;接收器和天线组成。图2.2给出了一种简化的单通道连续波雷达结构[2]。这…

什么是网络丢包以及如何解决

丢包的概念一直是网络行业争论的话题&#xff0c;在设计和实现网络时&#xff0c;它始终是考虑的关键因素&#xff0c;其重要性在于它对网络和网络系统的效率和整体性能的直接影响&#xff0c;即使是单个故障设备或配置错误的设置也会导致数据包丢失&#xff0c;也会严重影响整…

2 Mycat2 安装与启动

1、制作安装包 Mycat2不提供安装包,只提供核心JAR包,JAR包可以独立运行,安装包是使用Java Service Wrapper做壳的,如果需要安装包&#xff0c;需要自己制作。JAR可以作为Java库引入自己业务项目中使用Mycat2中的各个组件的设计都是可以独立使用的。 步骤如下&#xff1a; 1.…

【C++干货铺】继承后的多态 | 抽象类

个人主页点击直达&#xff1a;小白不是程序媛 C系列专栏&#xff1a;C干货铺 代码仓库&#xff1a;Gitee 目录 多态的概念 多态的定义和实现 多态的定义条件 虚函数 虚函数的重写 特殊情况 协变&#xff08;基类和派生类的虚函数返回值不同&#xff09; 析构函数的重…

ffmpeg踩坑之手动编译报错Unrecognized option ‘preset‘及rtsp/rtmp推流

本文解决的问题记录&#xff1a; 报错1&#xff1a;Unrecognized option preset. Error splitting the argument list: Option not found 报错2&#xff1a;ERROR: x264 not found using pkg-config 报错3&#xff1a;ffmpeg: error while loading shared libraries: libavd…

【linux】Debian不能运行sudo的解决

一、问题&#xff1a; sudo: 没有找到有效的 sudoers 资源&#xff0c;退出 sudo: 初始化审计插件 sudoers_audit 出错 二、可用的方法&#xff1a; 出现 "sudo: 没有找到有效的 sudoers 资源&#xff0c;退出" 和 "sudo: 初始化审计插件 sudoers_audit 出错&q…

spring面试:一、面试题分类总览+bean线程安全问题+AOP相关问题(定义、使用步骤、编程式事务管理和声明式事务管理和声明式事务管理失效)

面试题分类总览 bean线程安全问题 单例/多例 单例&#xff08;singleton&#xff09;&#xff1a;在每个spring ioc容器中都只有一个实例。 多例&#xff08;prototype&#xff09;&#xff1a;在每个spring ioc容器中有多个实例。 默认情况下spring中的bean都是单例的。但是…