Java链表(1)

news2024/10/5 15:28:26

🐵本篇文章将对单链表进行讲解,模拟实现单链表中常见的方法


一、什么是链表

链表是一种逻辑结构上连续而物理结构上不一定连续的线性表,链表由一个一个节点组成:

每一个节点中都由数据域(val)和指针域(next)组成,数据域用于存放要存放在该节点的数据,指针域用于存放节点的地址,上图中链表的每一个节点的指针域存放的是下一个节点的地址,最后一个节点的指针指向空

二、链表的种类

链表细分可以分为八种,分别为:

1. 单向不带头非循环链表 2. 单向不带头循环链表 

3. 单向带头非循环链表     4. 单向带头循环链表

5. 双向不带头非循环链表 6. 双向不带头循环链表 

7. 单向带头非循环链表     8. 双向带头循环链表

三、链表的模拟实现

首先对第一种单链表的增删查改等操作用Java进行模拟实现,先将要模拟实现的方法写到IList接口中:

public interface IList {
    //头插法
    public void addFirst(int data);

    //尾插法
    public void addLast(int data);

    //任意位置插入,第一个数据节点为0号下标
    public void addIndex(int index,int data);

    //查找关键字key是否在单链表当中
    public boolean contains(int key);

    //删除第一次出现关键字为key的节点
    public void remove(int key);

    //删除所有值为key的节点
    public void removeAllKey(int key);

    //得到单链表的长度
    public int size();

    //清空链表
    public void clear();

    //遍历并展示链表中的数据
    public void display();
}

之后再创建一个MySingleList类实现上述接口并重写接口中所有的方法

public class MySingleList implements IList{
    static class ListNode {
        public int val;
        public ListNode next;
        public ListNode(int val) {
            this.val = val;
        }
    }

    public ListNode head; //指向第一个节点
    
    /*以下是要重写IList接口中的方法*/
    ...

}

在MySingleList类中创建一个静态内部类ListNode用于描述一个节点对象,val用于存放数据,next用于存放下一个节点的地址,构造方法用于初始化数据va,接下来对上述方法进行模拟实现

3.1 方法的实现

public void addFirst(int data);

在链表的头部新增一个节点newNode

很明显要在头部新增一个节点就应让newNode的next指向head,并让newNode成为head

    public void addFirst(int data) {
        ListNode newNode = new ListNode(data);
        newNode.next = head;
        head = newNode;
    }

public void addLast(int data);

在链表的尾部新增一个节点

应让最后一个节点的next引用指向newNode,链表无法像顺序表一样可以直接找到某个位置的节点,所以应该通过循环先找到最后一个节点:

ListNode cur = head; //通常一个链表的头指针不动,需要定义一个cur来遍历链表、

while (cur.next != null) {
    cur = cur.next; //只要cur的next不是空,cur就指向下一个节点
}

//循环完成后cur就是最后一个节点
cur.next = newNode; 

完成以上操作后还要考虑一个特殊情况:如果链表为空,上述代码能否完成尾插操作?答案是不能的,因为如果链表为空,则head为空,cur为空,在循环条件处cur.next != null就会抛出一个NullPointerException的空指针异常,所以要单独处理链表为空的情况:

    public void addLast(int data) {
        ListNode newNode = new ListNode(data);
        if (head == null) { //如果链表为空,则直接让head指向newNode
            head = newNode;
        } else {
            ListNode cur = head;
            while (cur.next != null) {
                cur = cur.next;
            }
            cur.next = newNode;
        }
    }

public int size();

求链表的长度也就是该链表有几个节点,只要直接遍历链表即可

    public int size() {
        ListNode cur = head;
        int count = 0;
        while(cur != null) { //注意这里和尾插的循环条件不一样
            count++;
            cur = cur.next;
        }

        return count;
    }

public boolean contains(int key);

判断链表中是否有节点的val值等于key,也是只要直接遍历链表即可

    public boolean contains(int key) {
        ListNode cur = head;
        while(cur != null) {
            if (cur.val == key) {
                return true;
            }
            cur = cur.next;
        }
        return false;
    }

public void addIndex(int index,int data);

在任意位置处插入一个节点,第一个节点的索引为0

首先要判断一下index是否合法:

public class IndexException extends RuntimeException{
    public IndexException(String message) {
        super(message);
    }
}

=======================================================

public void addIndex(int index, int data) {
    if (index < 0 || index > size()) {
        throw new IndexException("下标错误");
    }
    ...
}

在任意位置插入节点,所以如果index为0或等于链表长度,就可以直接使用刚刚实现过的头插和尾插方法

public void addIndex(int index, int data) {
    if (index < 0 || index > size()) {
        throw new IndexException("下标错误");
    }
    if (index == 0) {
        addFirst(data);
    }
    if (index == size()) {
        addLast(data);
    }

    ...
}

然后就是一般情况下:

如果要在1位置处插入新节点,那就应该先找到1位置处的节点的前驱节点,这里定义为cur,令newNode.next = cur.next;这样newNode的next就指向1位置处的节点,之后令cur.next = newNode这样就插入完成了

    public void addIndex(int index, int data) {
        if (index < 0 || index > size()) {
            throw new IndexException("下标错误");
        }
        if (index == 0) {
            addFirst(data);
        }
        if (index == size()) {
            addLast(data);
        }

        ListNode newNode = new ListNode(data);
        ListNode cur = head;

        for (int i = 0; i < index - 1; i++) { 找到插入节点的前驱节点
            cur = cur.next;
        }

        newNode.next = cur.next;
        cur.next = newNode;
    }

public void remove(int key);

假如要删除val = 20的节点(这里先定义为del)如果要删除该节点就应让del的前驱节点的next指向del的后继节点:

    public void remove(int key) {
        ListNode cur = head;

        while(cur.next.val != key) { //通过循环找到del的前驱节点
            cur = cur.next; 
        }
        cur.next = cur.next.next;
    }

写完后还要考虑两种特殊情况:

第一,如果链表为空,上述代码不能满足要求,因为在循环条件处会抛出空指针异常

第二,如果要删除的节点是第一个节点,上述代码也不能满足要求,因为该循环是从第一个节点的下一个节点开始判断的

以上两种情况都需要单独处理:

    public void remove(int key) {
        if (head == null) {
            return;
        }
        if (head.val == key) {
            head = head.next;
            return;
        }
        ListNode cur = head;
        while(cur.next.val != key) {
            cur = cur.next;
        }
        cur.next = cur.next.next;
    }

public void removeAllKey(int key);

删除所有val = key的节点,那就需要遍历整个链表,在遍历过程中删除节点:

    public void removeAllKey(int key) {

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

    }

在循环的结束也要判断一下第一个节点是否为要删除的节点,因为上述循环条件为cur.next != null没有判断第一个节点:

    public void removeAllKey(int key) {
        if (head == null) { //如果链表为空则直接返回
            return;
        }

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

public void clear();

清空链表,由于对象在没有被引用时,就会自动被回收,所以只需要将head置为空就行了

public void clear() {
    head = null;
}

🙉本篇文章到此结束,下篇文章将对LinkedList类进行讲解

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

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

相关文章

食品加工厂可视化视频AI智能监管方案,助力工厂数字化运营

一、背景与需求分析 随着科技的不断进步和人们对食品安全和质量的日益关注&#xff0c;食品智慧工厂的建设成为了食品行业的一个重要趋势。智能化的食品工厂可以利用先进的技术和自动化系统&#xff0c;提高生产效率、降低监管成本&#xff0c;并确保产品的质量和安全。 行业…

使用GtkSharp下载zip包过慢问题解决方案

背景 安装GtkSharp这个包准备使用C#进行跨平台窗体应用程序开发&#xff0c;运行时发现其需要从github上下载【https://github.com/GtkSharp/Dependencies/raw/master/gtk-3.24.24.zip】这个依赖包&#xff0c;不知道是被墙了还是咋的&#xff0c;下载超时导致运行失败。 解决…

泛型..

1.泛型 所谓泛型 在类定义处是一种类型参数(我们平常所见到的参数指的就是方法中的参数 他接收有外界传递来的值 然后在方法中进行使用) 在类内部的话 则充当一种占位符 并且还提高了代码的复用率 何以见得提高了代码的复用率 其实就是通过对比使用了泛型技术和没有使用泛型技…

VS2019下各种报错合集(持续更新)

VS2019下的各种报错处理(长期更新)&#xff0c;欢迎大家在评论区补充错误代码/描述 解决方案&#xff01;&#xff01;&#xff01; 1、printf 代码运行到printf函数打印不出来内容&#xff0c;打断点之后&#xff0c;f10走过去&#xff0c;程序直接运行起来了&#xff0c;而…

Linux下串口編程遇到的接收数据错误及原因(

近日在调试串口的时候发现&#xff0c;另一设备向我ARM板的串口发送0x0d&#xff0c;我接收之后变成了0x0a&#xff0c;这是问题一&#xff1b;另外当对方向我发送一串数据&#xff0c;如果其中有0x11&#xff0c;那么我总是漏收此数&#xff0c;这是问题二。 由于问题莫名其妙…

深度视觉目标跟踪进展综述-论文笔记

中科大学报上的一篇综述&#xff0c;总结得很详细&#xff0c;整理了相关笔记。 1 引言 目标跟踪旨在基于初始帧中指定的感兴趣目标( 一般用矩形框表示) &#xff0c;在后续帧中对该目标进行持续的定位。 基于深度学习的跟踪算法&#xff0c;采用的框架包括相关滤波器、分类…

【SVA断言_2023.01.24】

在RTL设计中&#xff0c;仿真时查看异常情况&#xff0c;异常出现时&#xff0c;断言会报警&#xff0c;断言占整个设计的比例应不少于30% assertion作用&#xff1a; 检查特定条件或事件序列的出现情况提供功能覆盖 断言失败的严重程度&#xff1a;$fatal&#xff08;中止仿…

Oracle RAC 集群的安装(保姆级教程)

文章目录 一、安装前的规划1、系统规划2、网络规划3、存储规划 二、主机配置1、Linux主机安装&#xff08;rac01&rac02&#xff09;2、配置yum源并安装依赖包&#xff08;rac01&rac02&#xff09;3、网络配置&#xff08;rac01&rac02&#xff09;4、存储配置&#…

FitSM与ITIL及ITSM的对比

FitSM 是一种轻量级 IT 服务管理&#xff08;ITSM&#xff09;标准&#xff0c;其所有材料都是免费提供的&#xff0c;旨在促进 IT 服务提供中的服务管理&#xff0c;包括联合场景。但是&#xff0c;FitSM 适合您的组织吗&#xff1f;了解 FitSM 的适用性涉及两个方面。首先需要…

阿里云PAI-DSW部署ChatGLM3过程记录

1、申请免费的阿里云 PAI-DSW 资源&#xff0c;这个比较基础不做介绍了 2、从第二步开始&#xff0c;我们介绍部署ChatGLM3过程&#xff1a;PAI-DSW 资源界面如下图&#xff1a;点击开启新终端 3、默认目录下面建一个test目录&#xff0c;如下图&#xff1a; 4、执行命令cd te…

TPCC-MySQL

简介 TPC-C是专门针对联机交易处理系统&#xff08;OLTP系统&#xff09;的规范&#xff0c;一般情况下我们也把这类系统称为业务处理系统。 Tpcc-mysql是percona基于TPC-C(下面简写成TPCC)衍生出来的产品&#xff0c;专用于MySQL基准测试。其源码放在launchpad上&#xff0c…

2023年NOC大赛(学而思赛道)创意编程Python初中组决赛真题

2023年NOC大赛&#xff08;学而思赛道&#xff09;创意编程Python初中组决赛真题 题目总数&#xff1a;7 总分数&#xff1a;100 编程题 第 1 题 问答题 二进制回文 编程实现: 输入一个正整数&#xff0c;判断它的二进制形式是否是回文数&#xff0c;如果是输出True…

Web开发5:第三方扩展与部署

在Web开发中&#xff0c;第三方扩展和部署是提高开发效率和功能扩展的重要手段。第三方扩展可以帮助我们快速集成常用功能和工具&#xff0c;而部署则是将我们的应用程序发布到生产环境中。本文将介绍第三方扩展的重要性、如何选择和使用常见的第三方扩展&#xff0c;并讨论应用…

code server安装使用教程

1. 安装 1.1. 下载code-server安装包 类似这种文件&#xff1a;code-server-3.10.2-linux-amd64.tar.gz 解压&#xff1a;tar -xvf code-server-3.10.2-linux-amd64.tar.gz 1.2 &#xff08;可选&#xff09;建立软连接 ln -s path/to/code-server-3.10.2-linux-amd64/bin…

力扣15、三数之和(中等)

1 题目描述 图1 题目描述 2 题目解读 在整数数组nums中&#xff0c;找出三元组&#xff0c;它们的和为0&#xff0c;要求返回所有和为0且不重复的三元组。这是两数之和的扩展题目&#xff0c;可以将三数之和问题。 3 解法一&#xff1a;排序 双指针 将整数数组排序之后&#…

阿里云快速搭建《幻兽帕鲁》服务器自建指南

如何自建幻兽帕鲁服务器&#xff1f;基于阿里云服务器搭建幻兽帕鲁palworld服务器教程来了&#xff0c;一看就懂系列。本文是利用OOS中幻兽帕鲁扩展程序来一键部署幻兽帕鲁服务器&#xff0c;阿里云百科aliyunbaike.com分享官方基于阿里云服务器快速创建幻兽帕鲁服务器教程&…

使用javadoc生成maven项目的文档

概述&#xff1a;Maven 提供了 javadoc 插件来执行这个任务。 废话不多说&#xff0c;让我们开始操作吧&#xff01;&#xff01;&#xff01; 第一步&#xff1a;引入插件 在 pom.xml 中配置 javadoc 插件&#xff1a; 在 Maven 项目的 pom.xml 文件中&#xff0c;你需要添加…

AutoPSA的计算结果

1.中煤集团某用户问:请问&#xff0c;我导出来的.psa文件&#xff0c;在我同事另一台电脑上计算应力 怎么跟我电脑上的数据受力还有应力完全不一样呢? 原来&#xff0c;用户同事用的版本是9.3.5&#xff0c;用户用的版本是10.3.用户把.psa文件发给我们测试后&#xff0c;9.3…

Oracle篇—分区索引的重建和管理(第三篇,总共五篇)

☘️博主介绍☘️&#xff1a; ✨又是一天没白过&#xff0c;我是奈斯&#xff0c;DBA一名✨ ✌✌️擅长Oracle、MySQL、SQLserver、Linux&#xff0c;也在积极的扩展IT方向的其他知识面✌✌️ ❣️❣️❣️大佬们都喜欢静静的看文章&#xff0c;并且也会默默的点赞收藏加关注❣…

LeetCode:1706. 球会落何处(Java 模拟)

目录 1706. 球会落何处 题目描述&#xff1a; 实现代码与解析&#xff1a; 原理思路&#xff1a; 1706. 球会落何处 题目描述&#xff1a; 用一个大小为 m x n 的二维网格 grid 表示一个箱子。你有 n 颗球。箱子的顶部和底部都是开着的。 箱子中的每个单元格都有一个对角线…