Java中的链表

news2024/9/23 4:20:32

文章目录

  • 前言
  • 一、链表的概念及结构
  • 二、单向不带头非循坏链表的实现
    • 2.1打印链表
    • 2.2求链表的长度
    • 2.3头插法
    • 2.4尾插法
    • 2.5任意位置插入
    • 2.6查找是否包含某个元素的节点
    • 2.7删除第一次出现这个元素的节点
    • 2.8删除包含这个元素的所以节点
    • 2.9清空链表
    • 单向链表的测试
  • 三、双向不带头非循坏链表的实现
    • 3.1打印双向链表
    • 3.2求双向链表的长度
    • 3.3头插法
    • 3.4尾插法
    • 3.5任意位置插入
    • 3.6查找是否包含某个元素的节点
    • 3.7删除第一次出现这个元素的节点
    • 3.7删除包含这个元素的所有节点
    • 3.9清空双向链表
    • 双向链表的测试
    • LinkedList的遍历方式
    • 四、ArrayList和LinkedList的区别


前言

在前面我们已经学习了关于顺序表ArrayList的一些基本操作。通过源码知道,ArrayList底层使用数组来存储元素,由于其底层是一段连续空间,当在ArrayList任意位置插入或者删除元素时,就需要将后序元素整体往前或者往后搬移,时间复杂度为O(n),效率比较低,因此ArrayList不适合做任意位置插入和删除比较多的场景。因此:java集合中又引入了LinkedList,即链表结构


一、链表的概念及结构

链表是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的
注意:
1.链式结构在逻辑上是连续的,但是在物理上不一定连续
2.现实中的结点一般都是从堆上申请出来的
3.从堆上申请的空间,是按照一定的策略来分配的,两次申请的空间可能是连续,也可能不连续

链表的结构有8种样式:
单向带头循坏、单向带头非循坏、单向不带头循坏、单向不带头非循坏
双向带头循坏、双向带头非循坏、双向不带头循坏、双向不带头非循坏

这里我们主要学习以下两中结构:
单向不带头非循坏
在这里插入图片描述
LinkedList底层使用的就是双向不带头非循坏
在这里插入图片描述

二、单向不带头非循坏链表的实现

2.1打印链表

不带参数的打印

public void display() {
    ListNode cur = head;
    if(cur != null) {//遍历完所以节点
        System.out.print(cur.val+" ");
        cur = cur.next;
    }
    System.out.println();
}

带参数的打印

public void display(ListNode newHead) {
    ListNode cur = newHead;
    if(cur != null) {
        System.out.print(cur.val+" ");
        cur = cur.next;
    }
    System.out.println();
}    

2.2求链表的长度

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

2.3头插法

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

2.4尾插法

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;
    }
}

2.5任意位置插入

在任意位置插入时我们要判断该位置是否合法,不合法的时候要抛一个异常

  public void addIndex(int index,int data){
      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 = serchIndex(index);
      node.next = cur.next;
      cur.next = node;
}    

找要添加节点位置的前一个节点

public ListNode serchIndex(int index) {
    ListNode cur = head;
    int count = 0;
    while (count != index-1) {
        cur = cur.next;
        count++;
    }
    return cur;
}    

2.6查找是否包含某个元素的节点

遍历这个链表找是否与这个元素相同

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

2.7删除第一次出现这个元素的节点

public void remove(int key){
    if(head == null) {
        return;
    }
    if(head.val == key) {
        head = head.next;
        return;
    }
    ListNode cur = findKey(key);
    if(cur == null) {
        return;//没有要删除的元素
    }
    ListNode del = cur.next;
    cur.next = del.next;
}    

要删除节点的前一个节点

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

2.8删除包含这个元素的所以节点

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;
    }
}    

2.9清空链表

清空链表只需要把头节点置为空

public void clear() {
    head = null;
}    

单向链表的测试

public class Test {
    public static void main(String[] args) {
        MySingleList list = new MySingleList();
        list.addLast(30);//尾插
        list.addLast(20);
        list.addLast(30);
        list.addLast(40);
        list.addLast(50);
        list.addFirst(100);//头插
        list.addIndex(2,15);//任意位置插入
        list.display();
        System.out.println("*****");
        System.out.println(list.contains(20));//查看是否包含某个节点
        System.out.println("*****");
        System.out.println(list.size());//求链表长度
        System.out.println("*****");
        list.remove(30);//删除第一个出现的节点
        list.display();
        list.removeAllKey(30);//删除包含这个元素的所以节点
        System.out.println("*****");
        list.display();
        System.out.println("*****");
        list.clear();//清空链表
        list.display();
    }
}

在这里插入图片描述

三、双向不带头非循坏链表的实现

3.1打印双向链表

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

3.2求双向链表的长度

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

3.3头插法

public void addFist(int data) {
    ListNode node = new ListNode(data);
    if(head == null) {//一个节点都没有的情况
        head = node;
        last = node;
    }else {
        node.next = head;
        head.prev = node;
        head = node;
    }
}    

3.4尾插法

public void addLast(int data) {
    ListNode node = new ListNode(data);
    if(head == null) {//一个节点都没有的情况
        head = node;
        last = node;
    }else {
        last.next = node;
        node.prev = last;
        last = node;
    }
}    

3.5任意位置插入

这里的插入与单向链表一样也需要判断该位置的合法性,不合法时抛一个异常

public void addIndex(int index,int data) {
    if(index < 0 || index > size()) {
        throw new IndexException("双向链表中index的位置不合法:"+index);
    }
    if(index == 0) {
        addFist(data);
    }
    if(index == size()) {
        addLast(data);
    }
    ListNode cur = findIndex(index);
    ListNode node = new ListNode(data);
    node.next = cur;
    cur.prev.next = node;
    node.prev = cur.prev;
    cur.prev = node;
}    

要添加节点的位置

public ListNode findIndex(int index) {
    ListNode cur = head;
    if(index != 0) {
        cur = cur.next;
        index --;
    }
    return cur;
}    

3.6查找是否包含某个元素的节点

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

3.7删除第一次出现这个元素的节点

因为数据结构是一门逻辑性非常严谨的学科,所以这里的删除需要考虑多种因素

public void remove(int key){
    ListNode cur = head;
    while (cur != null) {
        if(cur.val == key) {
            if(cur == head) {
                head = head.next;
                if (head != null) {
                    head.prev = null;
                }else {
                    //只有一个节点,而且是需要删除的节点
                    last = null;
                }
            }else {
                //删除中间节点
                if(cur.next != null) {
                    cur.next.prev = cur.prev;
                    cur.prev.next = cur.next;
                }else {
                    //删除尾巴节点
                    cur.prev.next = cur.next;
                    last = last.prev;
                }
            }
            return;
        }
        cur = cur.next;
    }
}    

3.7删除包含这个元素的所有节点

public void remove(int key){
    ListNode cur = head;
    while (cur != null) {
        if(cur.val == key) {
            if(cur == head) {
                head = head.next;
                if (head != null) {
                    head.prev = null;
                }else {
                    //只有一个节点,而且是需要删除的节点
                    last = null;
                }
            }else {
                //删除中间节点
                if(cur.next != null) {
                    cur.next.prev = cur.prev;
                    cur.prev.next = cur.next;
                }else {
                    //删除尾巴节点
                    cur.prev.next = cur.next;
                    last = last.prev;
                }
            }
        }
        cur = cur.next;
    }
}    

3.9清空双向链表

public void clear(){
    ListNode cur = head;
    while (cur != null) {
        ListNode curNext = cur.next;
        cur.prev = null;
        cur.next = null;
        cur = cur.next;
    }
    head = null;//头节点置空
    last = null;//尾巴节点置空
}    

双向链表的测试

public class Test {
    public static void main(String[] args) {
        MyLinkedList myLinkedList = new MyLinkedList();
        myLinkedList.addLast(12);//尾插法
        myLinkedList.addLast(45);
        myLinkedList.addLast(34);
        myLinkedList.addLast(45);
        myLinkedList.addFist(56);//头插法
        myLinkedList.addIndex(2,15);//任意位置插入
        myLinkedList.display();
        System.out.println(myLinkedList.size());//求双向链表的长度
        System.out.println("******");
        System.out.println(myLinkedList.contains(23));//查找是否包含某个元素的节点
        System.out.println("******");
        myLinkedList.remove(45);//删除第一次出现这个元素的节点
        myLinkedList.display();
        System.out.println("******");
        myLinkedList.removeAllKey(45);//删除包含这个元素的所以节点
        myLinkedList.display();
        System.out.println("******");
        myLinkedList.clear();//清空链表
        myLinkedList.display();
    }
}

在这里插入图片描述

LinkedList的遍历方式

关于LinkedList的遍历方式有四种:

public class Test {
    public static void main(String[] args) {
        LinkedList<Integer> list = new LinkedList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        System.out.println(list);
        //for循坏遍历
        for (int i = 0; i < list.size(); i++) {
            System.out.print(list.get(i)+" ");
        }
        System.out.println();
        System.out.println("*******");
        //foreach遍历
        for (int m : list) {
            System.out.print(m +" ");
        }
        System.out.println();
        System.out.println("*******");
        //使用迭代器——正向遍历
        ListIterator<Integer> it = list.listIterator();
        while (it.hasNext()) {
            System.out.print(it.next()+" ");
        }
        System.out.println();
        System.out.println("*******");
        //使用迭代器——反向遍历
        ListIterator<Integer> it2 = list.listIterator(list.size());
        while (it2.hasPrevious()) {
            System.out.print(it2.previous()+" ");
        }
        System.out.println();
    }
}    

在这里插入图片描述

四、ArrayList和LinkedList的区别

1.ArrayList在物理上是连续的,LinkedList在逻辑上连续,但在物理上不一定连续
2.ArrayList和LinkedList是两种不同的数据结构。ArrayList是基于动态数组的,而LinkedList则是基于链表的
3.当需要随机访问元素(如get和set操作)时,ArrayList效率更高,因为LinkedList需要逐个查找。但当进行数据的增加和删除操作(如add和remove操作)时,LinkedList效率更高,因为ArrayList在进行这些操作时需要移动大量数据
4.ArrayList需要手动设置固定大小的容量,使用方便但自由性低;而LinkedList能够随数据量变化而动态调整,自由性较高但使用较为复杂

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

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

相关文章

使用动画曲线编辑器打造炫酷的3D可视化ACE

前言 在制作3D可视化看板时&#xff0c;除了精细的模型结构外&#xff0c;炫酷的动画效果也是必不可少的。无论是复杂的还是简单的动画效果&#xff0c;要实现100%的自然平滑都是具有挑战性的工作。这涉及到物理引擎的计算和对动画效果的数学建模分析。一般来说&#xff0c;只…

前端页面显示的时间格式为:2022-03-18T01:46:08.000+00:00 如何转换为:年-月-日,并根据当前时间判断为几天前

由于后端每条博文的发表时间是以“xxxx—xx—xxxx:xx:xx”的形式显示的&#xff0c; 现在要在前端改成“xxxx年xx月xx日”的形式。 并对10分钟内发表的显示“刚刚”&#xff0c;对24小时内发表的显示“小时前”。 超过24小时&#xff0c;小于48小时&#xff0c;显示“1天前”。…

PFA容量瓶应用工业制造领域PFA定容瓶精确测量的重要性

容量瓶是保证科学、医学和工业等各个领域精确测量的重要工具。这些专门的容量瓶被设计用来在特定的温度下保持精确的液体体积&#xff0c;使它们成为在工作中需要高精确度的专业人士不可或缺的工具。在这份容量瓶终极指南中&#xff0c;今天我们来探讨下这些仪器的重要性&#…

ios苹果app应用程序录屏开发有哪些难点和注意点?

Hello&#xff0c;各位同学们好&#xff0c;我是咕噜铁蛋&#xff0c;老朋友们应该知道我经常关注并分享各种移动应用开发的技术和经验。在这篇文章中&#xff0c;铁蛋将为大家介绍分享苹果iOS录屏开发的难点和注意点&#xff01; 首先&#xff0c;让我们简单了解一下iOS录屏的…

如何在 Eolink Apikit 中发起 TCP/UDP 文档测试

TCP/UDP 是两种常用的网络传输协议。TCP 协议提供可靠的连接&#xff0c;而 UDP 协议提供不可靠的连接。 TCP 协议是面向连接的协议&#xff0c;在建立连接之前&#xff0c;客户端和服务器需要先握手。握手完成后&#xff0c;客户端和服务器之间就会建立一个可靠的连接。在连接…

记录今日将C语言的Windows程序更改为python语言Windows程序,实现子窗口控制,类似微信程序框架最简单的原型

基本思路 为什么要选择python制作Windows应用程序&#xff0c;主要就是源代码直接展示&#xff0c;发现问题随时修改&#xff0c;同时可以不断增加新的功能方便。 由于C语言的Windows程序中结构类型在python中不能使用&#xff0c; 因此我们按照ctypes模块指导意见继承structu…

微服务技术 RabbitMQ SpringAMQP P61-P76

B站学习视频https://www.bilibili.com/video/BV1LQ4y127n4?p61&vd_source8665d6da33d4e2277ca40f03210fe53a 文档资料: 链接&#xff1a;https://pan.baidu.com/s/1P_Ag1BYiPaF52EI19A0YRw?pwdd03r 提取码&#xff1a;d03r 一 初始MQ 1. 同步通讯 2. 异步通讯 3. MQ常…

低代码与自动化:加速软件开发的新趋势

低代码与自动化技术正在逐渐改变软件开发的面貌。随着科技的不断发展&#xff0c;传统的编程方式已经不再是唯一的选择。低代码和自动化技术正在为开发者提供更高效、更灵活的开发环境&#xff0c;使得软件开发变得更加简单、快速和高效。 低代码和自动化技术正在逐渐改变软件开…

理解JSX:提高前端开发效率的关键(上)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

百度搜索展现服务重构:进步与优化

作者 | 瞭东 导读 本文将简单介绍搜索展现服务发展过程&#xff0c;以及当前其面临的三大挑战&#xff1a;研发难度高、架构能力欠缺、可复用性低&#xff0c;最后提出核心解决思路和具体落地方案&#xff0c;期望大家能有所收货和借鉴。 全文4736字&#xff0c;预计阅读时间12…

高级C#技术(二)

前言 本章为高级C#技术的第二节也是最后一节。前一节在下面这个链接 高级C#技术https://blog.csdn.net/qq_71897293/article/details/134930989?spm1001.2014.3001.5501 匿名类型 匿名类型如其名&#xff0c;匿名的没有指定变量的具体类型。 举个例子&#xff1a; 1 创建…

MySQL数据库,视图、存储过程与存储函数

数据库对象&#xff1a; 常见的数据库对象&#xff1a; 视图&#xff1a; 视图是一种虚拟表&#xff0c;本身是不具有数据的占用很少的内存空间。 视图建立在已有表的基础上&#xff0c;视图赖以建立的这些表称为基表。 视图的创建和删除只影响视图本身&#xff0c;不影响对…

案例058:基于微信小程序的智能社区服务管理系统

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;SSM JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder X 小程序…

计算机设计大赛信息可视化设计的获奖经验剖析解读—基于本专栏文章助力4C大赛【全网最全万字攻略-获奖必读】

文章目录 一.中国大学生计算机设计大赛1.1赛道解读1.2 信息可视化设计小类介绍1.2 小类区别解读 二.信息可视化设计赛道获奖经验2.1 四小类作品预览2.1.1 数据可视化小类-优秀参赛作品展览2.1.2 信息图形设计小类-优秀参赛作品展览2.1.3 动态信息影像&#xff08;MG动画&#x…

2024免费mac苹果电脑系统电脑管家CleanMyMac X

macOS已经成为最受欢迎的桌面操作系统之一&#xff0c;它提供了直观、简洁的用户界面&#xff0c;使用户可以轻松使用和管理系统。macOS拥有丰富的应用程序生态系统&#xff1b;还可以与其他苹果产品和服务紧密协作&#xff0c;如iPhone、iPad&#xff0c;用户可以通过iCloud同…

理解JSX:提高前端开发效率的关键(下)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

spring 笔记五 SpringMVC的数据响应

文章目录 SpringMVC的数据响应SpringMVC的数据响应方式回写数据 SpringMVC的数据响应 SpringMVC的数据响应方式 页面跳转 直接返回字符串通过ModelAndView对象返回 回写数据 直接返回字符串返回对象或集合 返回字符串形式 直接返回字符串&#xff1a;此种方式会将返回的字符…

nodejs+vue+微信小程序+python+PHP血液中心管理平台的设计与实现-计算机毕业设计推荐

实现采血的完整功能&#xff0c;系统用户主要分为两类&#xff0c;一类是管理员&#xff0c;一类是采血工作人员。管理员主要对采血工作人员以及血库进行管理。派发账号给员工作为采血工作人员&#xff0c;对血库的出库入库进行信息化管理。采血工作人员主要完成采血工作。通过…

jmeter,通过Ant插件生成html报告,展示接口详细信息

一、下载Ant 下载地址&#xff1a;Apache Ant - 二进制发行版 二、安装 1、Ant环境变量 解压Ant目录&#xff1b;配置系统环境变量&#xff0c;添加ANT_PATH&#xff0c;值为D:\Software\Ant_plugIn\apache-ant-1.10.14配置系统环境变量Path&#xff0c;添加Ant路径 %ANT_H…

MyBatisPlus基础入门笔记

MyBatisPlus基础入门笔记&#xff0c;源码可见下载链接 大家阅读时可善用目录功能&#xff0c;可以提高大家的阅读效率 下载地址&#xff1a;MyBatisPlus源码笔记 初识MyBatisPlus 入门案例 SpringBoot整合MyBatis&#xff08;复习&#xff09; 创建SpringBoot工程勾选使用的…