【Java数据结构】详解LinkedList与链表(一)

news2024/11/24 8:00:07

🔒文章目录:

1.❤️❤️前言~🥳🎉🎉🎉

2.ArrayList的缺陷

3.链表的概念及结构

4.无头单向非循环链表的实现 

4.1成员属性

4.2成员方法

         createList 

display——打印链表 

addFirst——头插

 addLast——尾插

 size——获取单链表长度

addIndex——在任意位置插入 

 contains——判定是否包含某个元素

remove——删除第一次出现关键字为key的结点

 removeAll——删除所有值为key的结点 

clear——清空单链表 

 4.3完整代码及使用

  完整代码 

  完整代码的使用

5.总结


 1.❤️❤️前言~🥳🎉🎉🎉

Hello, Hello~ 亲爱的朋友们👋👋,这里是E绵绵呀✍️✍️。

如果你喜欢这篇文章,请别吝啬你的点赞❤️❤️和收藏📖📖。如果你对我的内容感兴趣,记得关注我👀👀以便不错过每一篇精彩。

当然,如果在阅读中发现任何问题或疑问,我非常欢迎你在评论区留言指正🗨️🗨️。让我们共同努力,一起进步!

加油,一起CHIN UP!💪💪

🔗个人主页:E绵绵的博客
📚所属专栏:

1. JAVA知识点专栏

        深入探索JAVA的核心概念与技术细节

2.JAVA题目练习

        实战演练,巩固JAVA编程技能

3.c语言知识点专栏

        揭示c语言的底层逻辑与高级特性

4.c语言题目练习

        挑战自我,提升c语言编程能力

📘 持续更新中,敬请期待❤️❤️

借鉴文章:Java【链表】详细图解/ 模拟实现+【LinkedList】常用方法介绍_java linkedlist方法-CSDN博客

2.ArrayList的缺陷

上篇文章已经熟悉了ArrayList的使用,并且进行了简单模拟实现。通过源码知道,ArrayList底层使用数组来存储元素

但由于其底层是一段连续空间,当在ArrayList任意位置插入或者删除元素时,就需要将后序元素整体往前或者往后 搬移,时间复杂度为O(n),效率比较低,因此ArrayList不适合做任意位置插入和删除比较多的场景。

所以:java 集合中又引入了LinkedList,即链表结构。

3.链表的概念及结构

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


 注意:

1.从上图可看出,链式结构在逻辑上是连续的,但是在物理上不一定连续

2.其结点一般都是从堆上申请出来的

3.从堆上申请的空间,是按照一定的策略来分配的,两次申请的空间可能连续,也可能不连续


🎯🎯实际中链表的结构非常多样,以下情况组合起来就有8种链表结构:


1. 单向或者双向


2.带头或者不带头


3. 循环或者非循环

虽然有这么多的链表的结构,但是我们重点掌握两种:


1.无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如 哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。

2.无头双向非循环链表:在Java的集合框架库中LinkedList底层实现就是无头双向非循环链表。

4.无头单向非循环链表的实现 

4.1成员属性

 要模拟实现链表,也得自己实现一个类,首先要考虑这个类中的成员属性

已经说过,链表是由多个结点 “链接” 而成的,那么需要定义一个 静态内部类 Node:其中有两个域:
📌值域 value :来记录这个结点的值
📌指针域 next:来记录下一个结点的地址

📌并且它还需要一个构造方法:在创建ListNode内部类的同时分配好value的值。

📌模拟的链表中还需要一个 成员变量:head 来记录当前链表的头结点。 

​
public class SingleLinkedList {
    static  class  ListNode{
      public   int value;
      public   ListNode next;

        public ListNode(int value) {
            this.value = value;
        }
    }
   public ListNode head;

​

 4.2成员方法

  createList 

该方法是我们自己为了方便独创的,实际链表方法中并不存在该方法。

 public void createList(){
        ListNode listNode1 = new ListNode(45);
        ListNode listNode2 = new ListNode(46);
        ListNode listNode3 = new ListNode(50);
        ListNode listNode4 = new ListNode(56);
        ListNode listNode5 = new ListNode(67);
        listNode1.next=listNode2;
        listNode2.next=listNode3;
        listNode3.next=listNode4;
        listNode4.next=listNode5;
        head=listNode1;
    }

该方法创建了一个内部节点为5个的无头单向非循环链表。(注意结尾head要指向ListNode1,否则该链表之后会自动被释放掉)

display——打印链表 

   注意:LinkedList 中不存在该方法,为了方便看测试结果

  public void display(){
    ListNode cur=head;
    while(cur!=null){
        System.out.print(cur.value+" ");
        cur=cur.next;
    }
      System.out.println();
 }

addFirst——头插

❗️❗️和顺序表不同,链表是由一个个结点组成的,而顺序表可以理解为一个数组
顺序表插入之前必须考虑数组是否以及满了,而链表只需要关心各个结点的next即可。

因为我们还有一个成员属性:head,是用来记录头结点的
所以链表的头插操作就是:
1️⃣new 一个结点 node,类型是 Node
2️⃣链接:把头结点的地址( head 的值)赋给 node 的指针域 next
3️⃣head 记录新的头结点

 
public void  addFirst(int a){
    ListNode listNode = new ListNode(a);
    listNode.next=head;
    head=listNode;
}

 addLast——尾插

尾插步骤:
1️⃣new 一个 node,类型是 Node
2️⃣找到尾结点
3️⃣链接:把 node 的值(也就是地址)赋给尾结点的指针域 next

🚗🚗🚗
如何找到尾结点呢❓
需要 从头结点开始,遍历链表,找到一个结点的指针域 next 域为 null,它就是尾结点✅

head 是用来标记头结点的,所以 head 不能随意更改
我们需要再定义一个 Node 类型的 cur,让 cur 遍历链表

当 cur 找到尾结点后,需要让此时的尾结点和新结点 node 连接上
即如下图:

并且我们还需要注意一个特殊情况:

cur 这个变量中存放的值 要更改为下一个结点的地址:cur = cur.next;但当cur == null 时,这里就会发生空指针异常❌,那么在实例中是否会出现这种情况?

如果 head 一开始就是 null ,也就是链表为空时,cur 就会被 head 赋值成 null,就会发生空指针异常。所以当链表为空时,就不需要遍历链表找尾结点,直接把 node 的值赋给 head 即可。


public void  addLast(int a){
    ListNode listNode = new ListNode(a);
    ListNode cur=head;
   if(head==null){
       head=listNode;
       return;
}
    while(cur.next!=null){
       cur=cur.next;
    }
    cur.next=listNode;
}

 size——获取单链表长度

   直接遍历链表即可

public int size(){
        int count=0;
        ListNode cur=head;
        while(cur!=null){
         cur=cur.next;
         count++;
        }
        return count;
    }

addIndex——在任意位置插入 

官方规定第一个数据的位置是0,和数组的位置(下标)规则一致

所以我们首先要判断 index 的合法性:index<0 || index >链表长度是不合法❌的


index 合法的情况下,如何在index位置插入删除呢❓
📌index == 0就是头插
📌index = 链表长度就是尾插
❗️主要是链表中间位置的插入和删除

要想在两个结点中间插入新结点,首先要找到这两个结点的地址
找到 index -1 结点的位置也就相当于找到了 index 结点的位置

找到其位置很简单,cur 遍历链表 index-1 次即可


具体插入步骤:

1️⃣node.next = prevIndex.next;
2️⃣prevIndex.next = node;

这两行不能交换位置❗️❗️❗️
如果先让 prevIndex.next = node;那么就会丢失 index 位置的那个结点↪️,此时 node.next = prevIndex.next 就相当于 node.next = node;代码会发生错误❌


注意还有一个特殊点当链表中无任何节点,为null时,无论index为何值,都会直接添加一个节点。


public void addIndex(int index,int a){
        if(head==null){
            ListNode listNode = new ListNode(a);
            head=listNode;
            return;
        }
        if(index<0||index>size()){
            System.out.println("位置不合法");
            //这里就不搞抛出异常了,我们简单点
            return;
        }
        if(index==0){
            addFirst(a);
            return;
        }
        if(index==size()){
            addLast(a);
            return;
        }
    ListNode listNode = new ListNode(a);
    ListNode cur=head;
    for (int i = 0; i <index-1 ; i++) {
          cur=cur.next;
    }
     listNode.next=cur.next;
     cur.next=listNode;
}

 contains——判定是否包含某个元素

比较简单,遍历这个数组即可

public void  contain(int key){
        ListNode cur=head;
        while(cur!=null){
            if(cur.value==key){
                System.out.println(true);
                return;
            }
            cur=cur.next;
        }
    System.out.println(false);
}

因为这里我们存放的是 int 类型的变量,但 LinkedList 当中是存放引用数据类型的
⚠️⚠️⚠️当表中是引用类型时,就不可以用“等号”比较,应该用 equals 方法

remove——删除第一次出现关键字为key的结点

1️⃣如果链表为空就不能再删了
2️⃣如果头结点就是要删除的 key 结点,直接 head 存放下一个结点的地址
3️⃣如果链表其他结点是要删除的 key 结点,要先找到 key 结点的前一个结点

当 key 结点的前一个结点的 next 不再存放 key 结点地址时,key 结点此后不会再被使用,会被系统自动回收🗑️

所以完整代码如下:

public void remove(int key){
        ListNode cur=head;
        if(head==null) {
            System.out.println("为空链表,不能进行删除操作");
            return;
        }
        if(cur.value==key) {
            head=head.next;
            return;
        }
        while(cur.next!=null){
            if(cur.next.value==key){
             cur.next=cur.next.next;
             return;
            }
           cur=cur.next;
        }
        System.out.println("不存在该数");
}

 removeAll——删除所有值为key的结点 

这里我们将cur从head处开始检验:

当cur的下一个节点中的值等于key时:    cur.next=cur.next.next,否则cur=cur.next。

最后检测完毕后还要看一下head处的值是否等于key,如果等于则将head=head.next。


完整代码如下:

public void removeAll(int key){
        if(this.head == null) {
            System.out.println("为空链表,不能进行删除操作");
            return;
        }
        ListNode cur = head;
        while(cur.next != null){
            if(cur.next.value == key){
                cur.next=cur.next.next;
            }
            else {
                cur = cur.next;
            }}

        if(head.value==key){
            head = head.next;
                }
            }

clear——清空单链表 

head 这个变量一直存放着链表的头结点位置,把head置空,就找不到此链表,那么链表中的所有结点都会被系统自动回收🗑️

    public void clear() {
        head = null;
    }

 4.3完整代码及使用

  完整代码 

public class SingleLinkedList {
    static  class  ListNode{
      public   int value;
      public   ListNode next;

        public ListNode(int value) {
            this.value = value;
        }
    }
   public ListNode head;

    public void createList(){
        ListNode listNode1 = new ListNode(45);
        ListNode listNode2 = new ListNode(46);
        ListNode listNode3 = new ListNode(50);
        ListNode listNode4 = new ListNode(56);
        ListNode listNode5 = new ListNode(67);
        listNode1.next=listNode2;
        listNode2.next=listNode3;
        listNode3.next=listNode4;
        listNode4.next=listNode5;
        head=listNode1;
    }
  public void display(){
    ListNode cur=head;
    while(cur!=null){
        System.out.print(cur.value+" ");
        cur=cur.next;
    }
      System.out.println();
 }

public int size(){
        int count=0;
        ListNode cur=head;
        while(cur!=null){
         cur=cur.next;
         count++;
        }
        return count;
    }
public void  contain(int key){
        ListNode cur=head;
        while(cur!=null){
            if(cur.value==key){
                System.out.println(true);
                return;
            }
            cur=cur.next;
        }
    System.out.println(false);
}

  

    public void  addFirst(int a){
    ListNode listNode = new ListNode(a);
    listNode.next=head;
    head=listNode;
}
public void  addLast(int a){
    ListNode listNode = new ListNode(a);
    ListNode cur=head;
   if(head==null){
       head=listNode;
       return;
}
    while(cur.next!=null){
       cur=cur.next;
    }
    cur.next=listNode;
}

public void addIndex(int index,int a){
        if(head==null){
            ListNode listNode = new ListNode(a);
            head=listNode;
            return;
        }
        if(index<0||index>size()){
            System.out.println("位置不合法");
            //这里就不搞抛出异常了,我们简单点
            return;
        }
        if(index==0){
            addFirst(a);
            return;
        }
        if(index==size()){
            addLast(a);
            return;
        }
    ListNode listNode = new ListNode(a);
    ListNode cur=head;
    for (int i = 0; i <index-1 ; i++) {
          cur=cur.next;
    }
     listNode.next=cur.next;
     cur.next=listNode;
}

public void remove(int key){
        ListNode cur=head;
        if(head==null) {
            System.out.println("为空链表,不能进行删除操作");
            return;
        }
        if(cur.value==key) {
            head=head.next;
            return;
        }
        while(cur.next!=null){
            if(cur.next.value==key){
             cur.next=cur.next.next;
             return;
            }
           cur=cur.next;
        }
        System.out.println("不存在该数");
}

    public void removeAll(int key){
        if(this.head == null) {
            System.out.println("为空链表,不能进行删除操作");
            return;
        }
        ListNode cur = head;
        while(cur.next != null){
            if(cur.next.value == key){
                cur.next=cur.next.next;
            }
            else {
                cur = cur.next;
            }}

        if(head.value==key){
            head = head.next;
                }
            }

       public void clear(){
           head=null;
           }

    }

  完整代码的使用

public class Test {
    public static void main(String[] args) {
        SingleLinkedList singleLinkedList = new SingleLinkedList();
        singleLinkedList.createList();
        singleLinkedList.display();
        System.out.println(singleLinkedList.size());
        singleLinkedList.contain(45);
        singleLinkedList.addFirst(12);
        singleLinkedList.addLast(67);
        singleLinkedList.addIndex(3, 22);
        singleLinkedList.display();
        singleLinkedList.remove(22);
        singleLinkedList.remove(12);
        singleLinkedList.display();
        singleLinkedList.removeAll(67);
        singleLinkedList.display();

        System.out.println("=======================");
        singleLinkedList.clear();//清空该链表
        singleLinkedList.display();
        System.out.println("已清空该链表");
        System.out.println("=======================");
    }}

5.总结

所以,我们在本文中详细讲述了链表的概念和结构,并成功模拟了无头非循环单链表的实现。在下篇文章中,我们将带来一些关于单链表的面试题。在此,我们诚挚地邀请各位大佬们为我们点赞、关注,并在评论区留下您宝贵的意见与建议。让我们共同学习,共同进步,为知识的海洋增添更多宝贵的财富!🎉🎉🎉❤️❤️💕💕🥳👏👏👏

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

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

相关文章

70 Realistic Mountain Environment Textures Cliff(70+张真实的山地环境纹理)

大量适合山区和其他岩石环境的纹理--悬崖、岩石、砾石等等 每个纹理都是可贴的/无缝的,并且完全兼容各种不同的场景--标准Unity地形、Unity标准着色器、URP、HDRP等等都兼容。 所有的纹理都是4096x4096,并包括一个HDRP掩码,以完全支持HDRP。 特点。 70种质地 70种材料 70个地…

000002 - Hadoop环境安装

Hadoop及其大数据生态圈 1. 背景2. 实践2.1 Linux服务器准备2.2 在其中一台服务器上安装JDK2.3 在其中一台服务器上安装HADOOP2.4 本地模式运行一个hadoop案例 3. 自动化部署 1. 背景 要搭建Hadoop集群环境&#xff0c;我们需要执行如下 准备三台Linux服务器&#xff0c;服务…

【Linux驱动】【手把手配置3568寄存器】点亮RK3568的一颗LED

【硬件】 3568的LED9 &#xff1a;引脚 GPIO0 B7 【配置GPIO的复用】 找配置复用关系的寄存器基地址、偏移地址、对应配置的GPIO。 查找&#xff1a;io -r -4 0xfdc2000c 系统设置的默认值 结果为1&#xff0c;意思是只有bit 0是1&#xff0c;其他全都为0。所以系统默认就是…

【面试干货】 非关系型数据库(NoSQL)与 关系型数据库(RDBMS)的比较

【面试干货】 非关系型数据库&#xff08;NoSQL&#xff09;与 关系型数据库&#xff08;RDBMS&#xff09;的比较 一、引言二、非关系型数据库&#xff08;NoSQL&#xff09;2.1 优势 三、关系型数据库&#xff08;RDBMS&#xff09;3.1 优势 四、结论 &#x1f496;The Begin…

论文合集整理推荐2024.6.4

论文合集整理推荐2024.6.4 原创 小王搬运工 时序课堂 2024-06-04 20:12 四川 ‍2012年论文合集&#xff1a;论文入口 ‍2019年论文合集&#xff1a;论文入口 2021年论文合集&#xff1a;论文入口 2022年论文合集&#xff1a;论文入口 2023年论文合集&#xff1a;论文入口…

数据结构---力扣 20.有效的括号 (C语言

1.链接&#xff1a; . - 力扣&#xff08;LeetCode&#xff09;【点击即可跳转】 思路&#xff1a; 使用 栈 来完成操作 1.左括号-- 入栈 2.右括号-- 判断出栈顶的左括号与右括号是否匹配 如果匹配-->继续 不匹配-->终止 代码中 栈 的基本实现&#xff0c;不在以下展示&…

两站图片滑动对比效果实现(VUE3)

像这种图片滑动对比的效果&#xff0c;网上还不少见吧&#xff0c;但是网上却不好找到完整现成的实现代码&#xff0c;我找到几个地方有类似的代码&#xff0c;但是都不好直接移植到代码里&#xff0c;因为很多都是使用原生htmlcssjs实现&#xff0c;太复杂了。反而不好应用到v…

【Python Cookbook】S02E04 文本模式的匹配和查找 match()、search()、findall() 以及 捕获组和 + 的含义

目录 问题解决方案讨论 问题 本文讨论一些按照特定的文本模式进行的查找和匹配。 解决方案 如果想要匹配的只是简单文字&#xff0c;通常我们使用一些内置的基本字符串方法即可&#xff0c;如&#xff1a;str.find()&#xff0c;str.startwith()&#xff0c;str.endswith() …

MySQL数据库数据恢复方案应对没有where误操作导致的大量数据更新或删除

&#x1f604; 19年之后由于某些原因断更了三年&#xff0c;23年重新扬帆起航&#xff0c;推出更多优质博文&#xff0c;希望大家多多支持&#xff5e; &#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Mi…

二分答案-acwing-102. 最佳牛围栏

题目传送门&#xff1a;t​​​102. 最佳牛围栏 - AcWing题库高质量的算法题库https://www.acwing.com/problem/content/104/ 解题思路 整体解析 按照题目要求我们要找到一块连续的区域&#xff0c;使其里面每块地里面的平均值最大&#xff0c;且这块区域的长度要大于f 二分处理…

STM32——ADC篇(ADC的使用)

一、ADC的介绍 1.1什么是ADC ADC&#xff08;Analogto-Digital Converter&#xff09;模拟数字转换器&#xff0c;是将模拟信号转换成数字信号的一种外设。比如某一个电阻两端的是一个模拟信号&#xff0c;单片机无法直接采集&#xff0c;此时需要ADC先将短租两端的电…

免费获取云服务器

这几天刚入手了阿贝云的 “免费云服务器 ” &#xff0c;接下来给大家讲讲如何免费注册阿贝云的免费云服务器 如何获取免费云服务器 打开阿贝云官网&#xff0c;注册并认证 即可以领取免费云服务器 阿贝云地址&#xff1a;https://www.abeiyun.com/ 服务器优势 永久免费&…

单点登录(SSO)前端怎么做

单点登录&#xff08;SSO&#xff09;前端怎么做 本文介绍单点登录&#xff08;SSO&#xff09;是什么&#xff0c;还有就是前端怎么做。 单点登录&#xff08;SSO&#xff09;是什么 单点登录&#xff08;SSO&#xff0c;Single Sign On&#xff09;&#xff0c;是在企业内部…

【Java笔记】第9章:三个修饰符

前言1. abstract&#xff08;抽象的&#xff09;2. static&#xff08;静态的&#xff09;3. final&#xff08;最终的&#xff09;结语 上期回顾:【Java笔记】第8章&#xff1a;面向对象的三大特性&#xff08;封装、继承、多态&#xff09; 个人主页&#xff1a;C_GUIQU 归属…

瑞鑫RK3588 画中画 OSD 效果展示

这些功能本来在1126平台都实现过 但是迁移到3588平台之后 发现 API接口变化较大 主要开始的时候会比较费时间 需要找到变动接口对应的新接口 之后 就比较好操作了 经过几天的操作 已实现 效果如下

项目工具|git相关

本博客暂时只作为个人资料&#xff0c;后续会进行完善&#xff0c;主要内容来自&#xff1a; 【【Git第一讲】&#xff1a;git分区与两个盒子的故事】 理解暂存区和未暂存区 git为什么要多一个暂存区&#xff1f;难道不能我把代码写完后就是未暂存区&#xff0c;然后直接提交…

ROS2从入门到精通4-3:全局路径规划插件开发案例(以A*算法为例)

目录 0 专栏介绍1 路径规划插件的意义2 全局规划插件编写模板2.1 构造规划插件类2.2 注册并导出插件2.3 编译与使用插件 3 全局规划插件开发案例(A*算法)常见问题 0 专栏介绍 本专栏旨在通过对ROS2的系统学习&#xff0c;掌握ROS2底层基本分布式原理&#xff0c;并具有机器人建…

docker bash: vi: command not found 修改文件无法使用 vi yum的方法

如题&#xff0c;被入坑很多次。也参考了很多的修复docker 中的vi yum等方法。最终都未解决。 因为要修改 已安装容器中的各类配置信息。无法使用vi yum很麻烦。除去使用docker 挂载文件方法外&#xff0c;还可以使用如下方法直接修改对应的配置文件信息。 如: 修改 logstas…

通过抑制治疗上调的环氧化酶-2来改善光动力性能的肿瘤归巢嵌合肽菱形体

引用信息 文 章&#xff1a;Tumor Homing Chimeric Peptide Rhomboids to Improve Photodynamic Performance by Inhibiting Therapy‐Upregulated Cyclooxygenase-2. 期 刊&#xff1a;Smal&#xff08;影响因子&#xff1a;13.3&#xff09; 发表时间&#xff1a…

赢单有秘诀,大模型智能陪练更懂你

随着数字化技术在营销场景的加速应用&#xff0c;产品营销节奏不断加快&#xff0c;消费者需求日益多元化、个性化&#xff0c;市场竞争日趋激烈。面对复杂多变的市场环境&#xff0c;企业新产品、新服务的推出速度大幅提升&#xff0c;产品知识更新愈加频繁&#xff0c;传统的…