【数据结构】链表LinkedList

news2025/7/28 6:21:37

1.ArrayList的缺陷

2.单链表的实现

3.LinkedList的使用(模拟实现)

我们之前介绍过ArrayList了,它的底层是数组,数组是一段连续的空间,当我们想要插入或者删除数据的时候,插入元素,就要让插入位置的元素整体都往后移动,删除元素同样要让后面的元素往前移动,当要进行元素很多的插入或者删除的时候,ArrayList是效率很低的,所以当我们要大量插入元素或者大量删除元素,不推荐使用ArrayList

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

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

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

链表我们之前也介绍过,它在物理空间上是不连续,但在逻辑上连续,也就是说它的元素所在的存储空间中不是连在一起的,不是说元素1空间后面就是元素2的空间,但它在逻辑上连续,元素1跟元素2相当于拿一根绳子连接,虽然不是紧挨着的,但通过元素1就能找到元素2,这就像我们生活中的火车,或火车的车厢都是链在一起的

 链表中的元素我们称为节点,一个节点包含着本身的值,和下一个节点的信息(下一个节点的地址),分别是value和next(对于单链表而言)


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

(1)带头不带头

(2)单向双向

(3)循环非循环

我们把他们排列组合一下就能排列出8中情况,我们重要学习的链表结构有两种

1.不带头单向非循环

2.不带头双向非循环

自然这两种是面试笔试中常考的,当然也是比较难懂的,因为给的条件不多嘛~

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

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

这里解释一下这几个词的含义

带头 | | 不带头:头的意思就是头节点的意思,头节点的值一般没有什么含义,它与第一个元素链接,它拥有第一个元素的地址,只要有头,我们就能轻松找到第一个元素,你可以把它当做一个虚拟节点(虚拟节点的操作经常用来解题)

单向 || 双向:什么叫做单向,我们不是说了嘛,每一个节点它都包含下一个节点的地址,单向顾名思义就是一个方向,你只能从1找到2,而不能返回来从2找到1,因为1有2的地址,2只有3的地址,没有1的地址,所以说它是单向的;那么双向就是可以从1找到2,也可以从2找到1啦,双向链表的节点中,包含了两个地址,一个是下一个节点的地址,另外一个是上一个节点的地址

 上图就是单链表和双链表的图解

循环 | | 非循环:循环的意思就是这个链表围成了一个圈圈,下面给图来看

这图一目了然吧~上图就是循环,非循环自然不多解释


 接下来来自定义实现一个单链表

下面直接给出源码

package LinkedList内容;

import ArrayList内容.PosIndexNotLegalException;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: 86152
 * Date: 2022-11-12
 * Time: 20:46
 */
public class SingleList {
    //创建节点:包含值和地址两部分
    static class Node{
        public int val;
        public Node next;
        public Node(int val){
            this.val = val;
        }
    }

    public Node head;//创建头节点

    //用穷举的方式创建链表
    public void createList(){
        Node node1 = new Node(1);
        Node node2 = new Node(2);
        Node node3 = new Node(3);
        Node node4 = new Node(4);
        node1.next = node2;
        node2.next = node3;
        node3.next = node4;
        head = node1;
    }


    //头插法:时间复杂度O(1)
    public void addHead(int value){
        Node node = new Node(value);
        node.next = head;
        head = node;
    }

    //尾插法:时间复杂度O(n)
    public void addTail(int value){
        Node node = new Node(value);
        //处理没有节点的情况
        if(head == null){
            head = node;
        }else {
            Node cur = head;
            while (cur.next != null){
                cur = cur.next;
            }
            cur.next = node;
        }
    }

    //遍历打印链表
    public void display(){
        Node cur = this.head;
        while (cur != null){
            System.out.print(cur.val + " ");
            cur = cur.next;
        }
        System.out.println();
    }

    //判断链表中是否存在key值
    public boolean contains(int key){
        Node cur = head;
        while (cur != null){
            if(cur.val == key){
                return true;
            }
            cur = cur.next;
        }
        return false;
    }

    //链表长度
    public int size(){
        int count = 0;
        Node cur = head;
        while (cur != null){
            count++;
            cur = cur.next;
        }
        return count;
    }

    //在指定位置插入数据,要判断位置合法性

    private void checkIndex(int index){
        if(index < 0 || index > size()){
            throw new IndexNotLegalException();
        }
    }
    public void addIndex(int index, int value){
        checkIndex(index);
        if(index == 0){
            addHead(value);
            return;
        }
        if(index == size()){
            addTail(value);
            return;
        }
        Node node = new Node(value);
        Node cur = head;
        for(int i = 0; i < index-1; i++){
            cur = cur.next;
        }
        node.next = cur.next;
        cur.next = node;
    }

    //删除第一次出现key的节点
    //先找到前驱
    private Node prevKey(int key){
        Node cur = this.head;
        while (cur.next != null){
            if(cur.next.val == key){
                return cur;
            }
            cur = cur.next;
        }
        return null;
    }
    public void remove(int key){

        //头节点要单独处理
        if(head.val == key){
            head = head.next;
            return;
        }

        Node cur = prevKey(key);
        if(cur == null){
            return;
        }
        Node del = cur.next;
        cur.next = del.next;
        //cur.next = cur.next.next;
    }

    //删除所有值为key的节点
    //先处理其他节点,最后再处理头节点
    //对于头节点的处理得放在后面处理,放在前面最后不好处理
    public void removeAllKey(int key){
        if(head == null){
            return;
        }

        Node prev = head;
        Node 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;
        }

    }

    //清空链表的所有节点
    public void clear(){
        //this.head = null; 简单粗暴

        Node cur = head;
        while (cur != null){
            Node curNext = cur.next;
            cur.next = null;
            cur = curNext;
        }
        head = null;
    }

    //递归实现链表的打印
    public void printList(Node head){
        if(head == null){
            return;
        }
        if(head.next == null){
            System.out.print(head.val+" ");
            return;
        }
        printList(head.next);
        System.out.print(head.val+" ");
    }

}

很多方法的细节都写在了注释里了~


我们下面来介绍一下LinkedList,我们在写题目的时候,通常少不了使用

LinkedList底层就是一个双向链表

双向链表的实现这里就不实现了,无非就是在单链表的基础上多加上一个prev,然后还有last(这是尾节点,和头节点head对应)

LinkedList的底层是双向链表结构,由于链表没有将元素存储在连续的空间中,元素存储在单独的节点中,然后通过引用将节点连接起来了,因此在在任意位置插入或者删除元素时,不需要搬移元素,效率比较高。

【说明】

 LinkedList没有实现RandomAccess接口,因此LinkedList不支持随机访问 


下面介绍一下LinkedList的使用

这边直接给代码了,解释写在了注释里面

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: 86152
 * Date: 2022-11-14
 * Time: 17:36
 */
public class LinkedListTest {


    //三种遍历方法
    public static void main(String[] args) {
        List<Integer> list = new LinkedList<>();
        list.add(1);
        list.add(1);
        list.add(1);
        list.add(1);
        list.add(1);
        list.add(19);
        list.add(10);
        System.out.println("======for=========");
        for(int i = 0; i < list.size(); i++){
            System.out.print(list.get(i) + " ");
        }
        System.out.println();
        System.out.println("========foreach========");
        for(Integer x : list){
            System.out.print(x + " ");
        }
        System.out.println();
        System.out.println("==========迭代器========");
        ListIterator<Integer> it = list.listIterator();
        while (it.hasNext()){
            System.out.print(it.next() + " ");
        }
        System.out.println();
        //反向迭代器打印
        ListIterator<Integer> rit = list.listIterator(list.size());
        while (rit.hasPrevious()){
            System.out.print(rit.previous() + " ");
        }
    }

    /**
     * LinkedList常用api
     * @param args
     */
    public static void main2(String[] args) {
        LinkedList<Integer> list = new LinkedList<>();
        list.add(1);
        list.add(10);
        list.add(18);
        list.add(13);
        list.add(2);
        list.add(3);//add默认尾插
        System.out.println(list);

        list.add(0,2);//add的构造方法,支持在指定元素插入
        System.out.println(list);

        //删除操作remove
        //list.remove();//不带参数的remove默认删除第一个元素
        //list.remove(1);//带一个参数表示删除指定位置元素
        //list.removeFirst();
        //list.removeLast();
        //System.out.println(list);

        //查询contains 判断元素是否存在
        System.out.println(list.contains(18));

        //找到第一次出现元素的下标
        System.out.println(list.indexOf(2));//找第一次出现的位置
        System.out.println(list.lastIndexOf(3));//找最后一次出现的位置

        //获取指定位置元素
        System.out.println(list.get(0));

        //更新指定位置元素
        list.set(0,99);
        System.out.println(list);

        //截取元素
        System.out.println(list.subList(0, 2));//区间是前闭后开

        //还可以用截取的元素重新创建一个链表
        List<Integer> list2 = list.subList(0,3);
        System.out.println(list2);

        //清空元素
        list.clear();
        System.out.println(list.size());

    }

    /**
     * 构造方法
     * @param args
     */
    public static void main1(String[] args) {
        //一种是无参的构造方法
        LinkedList<Integer> list = new LinkedList<>();

        //第二种:LinkedList的构造方法可以传入ArrayList
        ArrayList<Integer> arrayList = new ArrayList<>();
        arrayList.add(1);
        arrayList.add(2);
        arrayList.add(3);

        LinkedList<Integer> linkedList = new LinkedList<>(arrayList);
        System.out.println(linkedList);

    }
}

 (部分图片来源:比特高博)

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

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

相关文章

用树莓派PICO做一个桌面时钟超详细教程!

用树莓派PICO做一个可显示时间和温湿度的桌面时钟一、概述二、材料准备1、树莓派PICO2、DHT11温湿度传感器3、DS1302时钟模块&#xff08;选用&#xff09;4、SSD1306屏幕5、其他材料三、开始1、连线2、写程序&#xff08;1&#xff09;使用内置RTC函数实现的时钟&#xff08;2…

2.11 教你一套怎么建立自己的选题素材库的方法【玩赚小红书】

一、自身定位延伸选题库 建立选题库&#xff0c;前提先确定自身定位&#xff0c;然后发散性思维延展。如我们做母婴博主&#xff0c;接下来就要想&#xff0c;母婴用户会关注什么&#xff0c;自然会想到到宝宝吃喝玩乐、妈妈保养、产后修复、婆媳关系等等内容。若我们只做宝宝…

这才是,真彩虹预染蛋白Markers

做WB的小伙伴都知道&#xff0c;现市面上各种“多彩”Marker的产品有很多&#xff0c;但是真正拿到手上的&#xff0c;可能是各种各样的&#xff08;见图1&#xff09;&#xff0c;咱也不清楚哪个是真的... 现在小编告诉你&#xff0c;经典的彩虹Marker长这样(见图2)&#xff1…

WebDAV之葫芦儿·派盘+读出通知

读出通知 支持webdav方式连接葫芦儿派盘。 手机各种推销通知太多,如何避免那些繁琐的通知内容,做出一键就能够阅读重要通知的最佳体验,帮助您更加快速和便捷的体验到那些应用内容?推荐大家使用读出通知。 读出通知APP可以设置接收通知的app,还可以用耳机操作,操作简单…

avalanche 少量tcp长连接持续构建HTTP请求测试

最近测试项目&#xff0c;测试要求使用少量tcp长连接连接&#xff0c;持续打HTTP请求&#xff0c;到测试结束。 分别用思博伦测试仪和supernova测试仪进行实现。 思博伦测试仪实现 测试仪基本运行流程&#xff1a;Loads配置任何形式bandwidth&#xff0c;connection&#xf…

SpringBoot项目本机和Linux环境部署

文章目录一. 本机环境下打包与运行二. Linux下部署SpringBoot项目2.1 Linux环境配置2.2 配置数据库2.3 运行程序一. 本机环境下打包与运行 项目进行打包 2. 本机环境下运行SpringBoot程序 控制台进入SpringBoot项目jar包所在的文件夹&#xff0c;运行下面指令即可 java -jar […

[附源码]java毕业设计企业招标系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

JDBC技术

JDBC 一、jdbc的概述 JDBC(Java Database Connectivity)是一个独立于特定数据库管理系统、通用的SQL数据库存取和操作的公共接口&#xff08;一组API&#xff09;&#xff0c;定义了用来访问数据库的标准Java类库&#xff0c;&#xff08;java.sql,javax.sql&#xff09;使用…

第十一周周报

学习目标&#xff1a; DDPM 学习内容&#xff1a; DDPM代码 学习时间&#xff1a; 11.13-11.18 学习产出&#xff1a; 一、DDPM 1、trainer trainer用来计算损失&#xff0c;即将图片加噪后计算损失&#xff0c;损失公式如下&#xff1a; extract()函数&#xff1a;…

基于HASM模型的土壤高精度建模matlab仿真

欢迎订阅《FPGA学习入门100例教程》、《MATLAB学习入门100例教程》 目录 一、理论基础 二、核心程序 三、测试结果 一、理论基础 土壤有机碳库是陆地生态系统中最丰富的碳库&#xff0c;其动态变化和存储分布在土壤质量评估、农田生态管理和气候变化适应与减缓等领域起着至关…

Java实现图书管理系统

作者&#xff1a;~小明学编程 文章专栏&#xff1a;JavaSE基础 格言&#xff1a;目之所及皆为回忆&#xff0c;心之所想皆为过往 今天给大家带来的是用Java实现的图书管理系统。 目录 需求 图书类 创建图书类 创建书架 Operation IOperation接口 添加图书AddOperation…

easyrecovery15最新版数据恢复类软件测评

当下如今&#xff0c;利用笔记本进行学习和办公已经是毋庸置疑的了&#xff0c;所以会需要在电脑上保存大量的数据信息&#xff0c;但是电脑在带来方便的同时&#xff0c;也存在很多的隐患。万一数据丢失了&#xff0c;该怎么办呢&#xff1f;要解决数据丢失问题&#xff0c;就…

VUE3 中实现拖拽和缩放自定义看板 vue-grid-layout

Vue Grid Layout官方文档 Vue Grid Layout中文文档 1. npm下载拖拽缩放库 npm install vue-grid-layout3.0.0-beta1 --save 2. vue3 使用 vue-grid-layout报错&#xff1a;external_commonjs_vue_commonjs2_vue_root_Vue_default.a is not a constructor 解决方案: vue3版本…

力扣刷题(代码回忆录)——数组部分

数组 数组过于简单&#xff0c;但你该了解这些&#xff01;数组&#xff1a;二分查找数组&#xff1a;移除元素数组&#xff1a;序数组的平方数组&#xff1a;长度最小的子数组数组&#xff1a;螺旋矩阵II数组&#xff1a;总结篇704. 二分查找 给定一个 n 个元素有序的&#…

什么是MQ

MQ概述 MQ全称 Message Queue&#xff08;消息队列&#xff09;&#xff0c;是在消息的传输过程中保存消息的容器。多用于分布式系统之间进 行通信。 分布式系统之间进行通信&#xff1a; 远程调用&#xff1a;各系统间直接通过远程调用的方式&#xff1b; 借助第三方完成系统…

【GlobalMapper精品教程】019:基于DSM提取离散随机点的高程信息

本文讲解在globalmapper中,基于DSM提取离散随机点的高程信息,配套数据为data019.rar。 文章目录 1. 离散点创建2. 提取离散点高程信息3. 高程标注1. 离散点创建 本文在ArcGIS中,根据给定的范围,随机生成离散点,如下图: 拓展阅读: ArcGIS根据范围创建随机点教程:【ArcG…

关于Kdo N3,1380099-68-2,3-脱氧-D-甘露-辛酸(Kdo)相关物理化学知识了解下

基础产品数据&#xff08;Basic Product Data&#xff09;&#xff1a; CAS号&#xff1a;1380099-68-2 中文名&#xff1a;2-酮基-3-脱氧辛酸叠氮糖 英文名&#xff1a;Kdo Azide&#xff0c;Kdo N3 结构式&#xff08;Structural&#xff09;&#xff1a; 试剂基团反应特点&a…

基于51单片机的波形发生器proteus仿真数码管LCD12864显示

仿真图1简介&#xff1a; 本系统采用51单片机作为系统的MCU&#xff08;具体型号见下图&#xff09;&#xff0c;该系统显示器为四位数码管&#xff0c;可实时显示波形的参数情况 可显示四种波形&#xff0c;分别是方波、正弦波、三角波、锯齿波。 该设计具有电压表功能&#…

C语言MFC导出dll回调函数方法详解

如何将回调函数导出来 这一章节主要讲述在导出函数的基础上如何将回调函数导出来。 C程序设计语言&#xff08;第1-3部分&#xff09;&#xff08;原书第4版&#xff09; 京东自营优惠价&#xff1a;&#xffe5;119.1立即抢购 回调函数的应用相信很多C程序猿儿们都不陌生吧…

弘玑Cyclone2022年产品发布会:人人可用的数字化工作平台——弘玑工作易

近日&#xff0c;在弘玑Cyclone“智无边界&#xff0c;数字未来”发布会上&#xff0c;弘玑Cyclone2022年超级自动化系列产品全新亮相&#xff0c;首席产品官贾岿博士带领产品团队以创新技术对新时代语境下的数字生产力进行了全新解读。 本文将为大家分享本次发布会重磅推出的…