Java集合类——LinkedList(单链表及双链表)

news2024/12/27 10:45:45

一,ArrayList的缺陷

1.空间浪费

在之前的博客中,我利用源码详细的讲解了ArrayList这个集合类(尤其是扩容机制),可以知道ArrayList的底层主要是一个动态的可变数组,容量满的时候需要进行1.5倍扩容。但是我们现在考虑这样一个问题,假设ArrayList底层数组的容量是100,我们需要存放101个元素时,当存放第101个元素时需要1.5倍扩容(扩容之后的容量是150),此时势必会造成49个存储空间的浪费!

2.时间开销大

ArrayList底层是一个数组,当我们对顺序表进行插入和删除时,需要移动大量的元素,大大提高了时间复杂度,所以可以看出ArrayList并不适用于进行大量插入和删除的操作。

针对上述ArrayList的两大缺陷,Java是否提供了其他的集合类或者数据结构来解决呢?

1.进行元素扩容时能否根据需求来进行扩容,需要多少空间就去申请多少空间;

2.在进行插入和删除的时候,可不可以不需要移动大量元素。

今天所要介绍的LinkedList集合类和链表的数据结构将很好的解决这两个问题!!!

二,链表

2.1 链表的概念

相对于顺序表,链表是一种在逻辑上连续,在存储上不一定连续的数据结构。其中链表每个元素之间的逻辑关系是通过引用来实现的。

链表中的每一个元素称为一个节点,每一个节点至少包含两类数据:

1.数据域(存放该节点的数据信息)

2.节点域(实现链表节点与节点之间的逻辑关系,对于单链表来说只需要存储一个next即可(存储下一个节点的地址),对于双链表来说则需要存储next和prev(分别存储下一个节点和前一个节点的地址))

2.2 链表的分类

1.单向或者双向

2.带头或者不带头

3.循环或者非循环

我们只需要重点掌握两种即可:

1.无头单向非循环链表(因为笔试的OJ题中经常会考);

2.无头双向链表(因为LinkedList的底层就是不带头的双链表)。 

三,顺序表和链表的区别

不同点
ArrayList
LinkedList
存储空间上
物理上一定连续
逻辑上连续,但物理上不一定连续
随机访问
支持: O(1)
不支持: O(N)
头插
需要搬移元素,效率低 O(N)
只需修改引用的指向,时间复杂度为 O(1)
插入
空间不够时需要扩容
没有容量的概念
应用场景
元素高效存储 + 频繁访问
任意位置插入和删除频繁

四,无头单向非循环链表的实现

定义一个MySingleList类来实现单链表的方法,用TestMySingleList类来测试MySingleList类的方法

import java.util.List;

public class MySingleList {
    static class ListNode {
        public int val;
        public ListNode next;

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

    ListNode head;//第一节点的引用,默认为null

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

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

    //头插法
    public void addFirst(int data) {
        ListNode node = new ListNode(data);
        node.next = this.head;
        this.head = node;
    }

    //尾插法
    public void addLast(int data) {
        ListNode node = new ListNode(data);
        ListNode cur = this.head;
        if (this.head == null) {
            this.head = node;
        } else {
            while (cur.next != null) {
                cur = cur.next;
            }
            //此时已经处于尾节点
            cur.next = node;
        }
    }

    //任意index位置插入(假设第一个节点的下表为0)
    public void add(int index, int data) {
        //判断index位置的合法性
        if (index < 0 || index > size()) {
            System.out.println("index位置不合法!");//也可以抛异常
        } else if (index == 0) {
            addFirst(data);
        } else if (index == size()) {
            addLast(data);
        } else {
            //1.找到所需插入index位置的前驱
            ListNode cur = findIndexPrev(index);
            //2.修改引用的指向
            ListNode node = new ListNode(data);
            node.next = cur.next;
            cur.next = node;
        }
    }

    private ListNode findIndexPrev(int index) {
        ListNode cur = this.head;
        while (index - 1 != 0) {
            cur = cur.next;
            index--;
        }
        return cur;
    }

    //删除第一次出现的关键字key
    public void remove(int key) {
        if (this.head == null) {
            System.out.println("链表为空!");
            return;
        }
        if (this.head.val == key) {
            this.head = this.head.next;
            return;
        }
        ListNode cur = findPrev(key);//找到所需删除关键字key的前驱
        if (cur == null) {
            System.out.println("没有关键字key!");
        } else {
            cur.next = cur.next.next;
        }
    }

    private ListNode findPrev(int key) {
        ListNode cur = this.head;
        while (cur.next != null) {
            if (cur.val == key) {
                return cur;
            }
            cur = cur.next;
        }
        return null;
    }

    //删除所有出现的关键字key
    public void removeAllKey(int key) {
        if (this.head == null) {
            System.out.println("链表为空!");
            return;
        }
        ListNode prev = this.head;
        ListNode cur = this.head.next;
        while (cur != null) {
            if (cur.val == key) {
                prev.next = cur.next;
                cur = cur.next;
            } else {
                prev = cur;
                cur = cur.next;
            }
        }
        if (this.head.val == key) {
            this.head = this.head.next;
        }
    }

    //清空链表
    public void clear() {
        this.head = null;//单链表只需要将指向第一个节点的引用指向null即可
    }
}
public class TestMySingleList {
    public static void main(String[] args) {
        MySingleList mySingleList = new MySingleList();
        mySingleList.addLast(1);
        mySingleList.addLast(2);
        mySingleList.addLast(3);
        mySingleList.addLast(4);
        mySingleList.addLast(5);
        mySingleList.display();
        mySingleList.addFirst(0);
        mySingleList.display();
        mySingleList.remove(0);
        mySingleList.addLast(1);
        mySingleList.addLast(1);
        mySingleList.addLast(1);
        mySingleList.removeAllKey(1);
        mySingleList.display();
    }
}

 

五,无头双向非循环链表的实现

定义一个MyLinkedList类来实现单链表的方法,用TestMyLinkedList类来测试MyLinkedList类的方法

public class MyLinkedList {
    static class ListNode {
        public int val;
        public ListNode prev;
        public ListNode next;

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

    ListNode head;//定义一个头引用,默认为null
    ListNode tail;//定义一个尾引用,默认为null

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

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

    //判断链表是否包含某个元素key
    public boolean contains(int key) {
        ListNode cur = this.head;
        while (cur != null) {
            if (cur.val == key) {
                return true;
            }
            cur = cur.next;
        }
        return false;
    }

    //头插法
    public void addFirst(int data) {
        ListNode node = new ListNode(data);
        if (this.head == null) {
            this.head = node;
            this.tail = node;
        } else {
            node.next = this.head;
            this.head.prev = node;
            this.head = node;
        }
    }

    //尾插法
    public void addLast(int data) {
        ListNode node = new ListNode(data);
        if (this.head == null) {
            this.head = node;
            this.tail = node;
        } else {
            this.tail.next = node;
            node.prev = this.tail;
            this.tail = node;
        }
    }

    //任意index位置插入(假设第一个节点的下标为0)
    public void add(int index, int data) {
        //判断index位置的合法性
        if (index < 0 || index > size()) {
            System.out.println("index位置不合法!");//也可以抛异常
        } else if (index == 0) {
            addFirst(data);
        } else if (index == size()) {
            addLast(data);
        } else {
            //1.找到index位置的节点
            ListNode cur = findIndexPrev(index);
            //2.修改指向的引用
            ListNode node = new ListNode(data);
            node.next = cur;
            node.prev = cur.prev;
            cur.prev.next = node;
            cur.prev = node;
        }
    }

    private ListNode findIndexPrev(int index) {
        ListNode cur = this.head;
        while (index != 0) {
            cur = cur.next;
            index--;
        }
        return cur;
    }

    //删除第一次出现的关键字key
    public void remove(int key) {
        if (this.head == null) {
            System.out.println("链表为空!");
            return;
        }
        ListNode cur = this.head;
        while (cur != null) {
            if (cur.val == key) {
                if (cur == this.head) {
                    this.head = this.head.next;
                    if (this.head != null) {
                        this.head.prev = null;
                    } else {
                        this.tail = null;
                    }
                } else {
                    cur.prev.next = cur.next;
                    if (cur.next != null) {
                        cur.next.prev = cur.prev;
                    } else {
                        this.tail = cur.prev;
                        this.tail.next = null;
                    }
                }
                return;
            }
            cur = cur.next;
        }
    }

    //删除所有出现的关键字key
    public void removeAllKey(int key) {
        if (this.head == null) {
            System.out.println("链表为空!");
            return;
        }
        ListNode cur = this.head;
        while (cur != null) {
            if (cur.val == key) {
                if (cur == this.head) {
                    this.head = this.head.next;
                    if (this.head != null) {
                        this.head.prev = null;
                    } else {
                        this.tail = null;
                    }
                } else {
                    cur.prev.next = cur.next;
                    if (cur.next != null) {
                        cur.next.prev = cur.prev;
                    } else {
                        this.tail = cur.prev;
                        this.tail.next = null;
                    }
                }
            }
            cur = cur.next;
        }
    }

    //清空链表
    public void clear() {
        ListNode cur = this.head;
        while (cur != null) {
            ListNode curNext = cur.next;
            cur.prev = null;
            cur.next = null;
            cur = curNext;
        }
        this.head = null;
        this.tail = null;
    }
}
public class TestMyLinkedList {
    public static void main(String[] args) {
        MyLinkedList myLinkedList = new MyLinkedList();
        myLinkedList.addLast(1);
        myLinkedList.addLast(2);
        myLinkedList.addLast(3);
        myLinkedList.addLast(4);
        myLinkedList.addLast(5);
        myLinkedList.addFirst(0);
        myLinkedList.display();
        myLinkedList.remove(0);
        myLinkedList.display();
        myLinkedList.add(2,10);
        myLinkedList.display();
        myLinkedList.addLast(1);
        myLinkedList.addLast(1);
        myLinkedList.removeAllKey(1);
        myLinkedList.display();
    }
}

 

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

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

相关文章

第二十讲:神州路由器静态路由的配置

实验拓扑图如下所示 设备 端口 IP 子网掩码 网关 Router-A G0/0 120.83.200.55 255.255.255.0 无 G0/3 192.168.0.1 255.255.255.0 无 Router-B G0/0 120.83.200.56 255.255.255.0 无 G0/3 192.168.1.1 255.255.255.0 无 PC1 192.168.0.2 255.255.255…

jQuery 的基本使用

1、jQuery 介绍 1.1、JavaScript 库 JavaScript库&#xff1a;即 library&#xff0c;是一个封装好的特定的集合&#xff08;方法和函数&#xff09;。从封装一大堆函数的角度理解库&#xff0c;就是在这个库中&#xff0c;封装了很多预先定义好的函数在里面&#xff0c;比如动…

【C++】const关键字

【C】const关键字 0x1 常量 C定义常量有两种方式 #define 宏常量&#xff1a;#define 常量名 常量值 通常在文件上方定义&#xff0c;表示一个常量宏常量不可以修改 // 宏常量 #define MAX 999int main() {return 0; }const修饰的变量&#xff1a; const 数据类型 常量名 …

docker 安装Es

1、下载镜像文件 docker pull elasticsearch:7.4.2 存储和检索数据 docker pull kibana:7.4.2 可视化检索数据 2、创建实例 1、ElasticSearch mkdir -p /mydata/elasticsearch/config mkdir -p /mydata/elasticsearch/data echo "http.host: 0.0.0.0" >…

第三十六章 数论——容斥原理

第三十六章 数论——容斥原理一、容斥原理1、定理内容二、代码模板1、问题&#xff08;1&#xff09;如何求出能够被整除的个数&#xff1f;&#xff08;2&#xff09;如何枚举出2n−12^n-12n−1种情况&#xff1f;2、代码实现&#xff1a;一、容斥原理 1、定理内容 我们在高…

开启微信小程序的学习窗口(第一课)

第一个问题 什么是微信小程序 微信小程序&#xff0c;小程序的一种&#xff0c;英文名Wechat Mini Program&#xff0c;是一种不需要下载安装即可使用的应用&#xff0c;它实现了应用“触手可及”的梦想&#xff0c;用户扫一扫或搜一下即可打开应用。 全面开放申请后&#xff0…

Educational Codeforces Round 93 (Rated for Div. 2) K. Lonely Numbers

Problem - C - Codeforces 翻译&#xff1a; 给定一个数组&#x1d44e;1&#xff0c;&#x1d44e;2&#xff0c;…&#xff0c;&#x1d44e;&#x1d45b;&#xff0c;由0到9的整数组成。一子数组&#x1d44e;&#x1d459;,&#x1d44e;&#x1d459; 1,&#x1d44e;&…

R实战 | 置换多元方差分析(以PCoA的PERMANOVA分析为例)

adonis-cover置换多元方差分析&#xff08;Permutational multivariate analysis of variance&#xff0c;PERMANOVA&#xff09;&#xff0c;又称非参数多因素方差分析&#xff08;nonparametric multivariate analysis of variance&#xff09;、或者ADONIS分析。它利用距离矩…

第003课 - 分布式基础概念

文章目录 集群、分布式、节点远程调用负载均衡服务注册/发现和注册中心服务熔断和降级API网关我们以前将所有的代码、页面、sql语句,写到一个应用,如果有一个地方有问题,整个就不可用了。 我们可以基于业务边界进行服务微化和拆分。 如果有一个出现了问题,不影响其他服务…

迅为LS2K0500开发板龙芯全国产处理器LoongArch架构核心主板

全国产开发板&#xff1a; 迅为iTOP-LS2K0500开发采用龙芯LS2K0500处理器&#xff0c;基于龙芯自主指令系统&#xff08;LoongArch&#xff09;架构&#xff0c;片内集成64位LA264处理器核、32位DDR3控制器、2DGPU、DVO显示接口、两路PCle2.0、两路SATA2.0、四路USB2.0、一路US…

梯度下降算法、随机梯度下降算法、动量随机梯度下降算法、AdaGrad算法、RMSProp算法、Adam算法详细介绍及其原理详解

相关文章 梯度下降算法、随机梯度下降算法、动量随机梯度下降算法、AdaGrad算法、RMSProp算法、Adam算法详细介绍及其原理详解反向传播算法和计算图详细介绍及其原理详解 文章目录相关文章前言一、回归拟合问题二、损失函数三、梯度下降算法四、随机梯度下降算法五、动量随机梯…

国际山岳日,周大福百年承诺续写永恒美好

纵横古今&#xff0c;俯瞰万里 每一寸绿野都孕育万物生机 每一座山林都彰示生命之本 百周年承诺 守护自然生态 周大福珠宝集团坚守“用真诚让幸福永恒“的企业理念 我们的百周年承诺包括对地球真诚且有效的付出服务 致力守护珍贵的大自然环境&#xff0c;为人类和星球幸福…

吉林优美姿文化:抖音怎么做爆款输出?

要知道&#xff0c;现在自媒体发展的越来越好了&#xff0c;其中发展的最好的就是抖音平台&#xff0c;大家如果要利用抖音平台达到引流的目的的话&#xff0c;也要去学习一下抖音相关的技巧&#xff0c;那么抖音怎么去买号呢&#xff1f;跟着吉林优美姿小编来一起看看吧&#…

亚马逊---人工智能入门---学习笔记

&#x1f680;write in front&#x1f680; &#x1f4dd;个人主页&#xff1a;认真写博客的夏目浅石. &#x1f381;欢迎各位→点赞&#x1f44d; 收藏⭐️ 留言&#x1f4dd;​ &#x1f4e3;系列专栏&#xff1a;蓝桥杯算法笔记 &#x1f4ac;总结&#xff1a;希望你看完之…

SpringBoot 的配置

目录 配置文件到底有什么作用呢 ? SpringBoot的配置文件的格式有哪些呢? properties配置文件 yml配置文件 properties乱码问题 多平台的配置文件设置 配置文件到底有什么作用呢 ? 配置文件主要是配置项目的一些重要的数据.. 比如配置数据库的连接信息 数据库是非常重…

虚拟机中如何安装Liunx环境

安装步骤 首先 准备一个Linux系统镜像 这是下载地址&#xff1a;https://cn.ubuntu.com/download/server/step1 然后打开虚拟机软件&#xff0c;点击新建 配置虚拟机名称 配置内存【建议4GB&#xff0c;内存小就少弄一顿】【再点击下一步】 硬盘配置 点击下一步 到这一步&am…

MVP、原型、概念验证,傻傻分不清楚?

MVP、原型以及概念验证这三者的概念虽然没有密切的联系&#xff0c;但也有不少人会分不清这三者的区别&#xff0c;在这篇文章中&#xff0c;我们会帮大家区分一下这三个概念。 首先是MVP&#xff0c;MVP是Minimum Viable Product的缩写&#xff0c;即最小可行性产品。MVP通过…

计算机网络---DHCP和自动配置

什么是DHCP HCP&#xff08;动态主机配置协议&#xff09;是一个局域网的网络协议&#xff0c;客户机 / 服务器协议。指的是由服务器控制一段IP地址范围&#xff0c;客户机登录服务器时就可以自动获得服务器分配的IP地址和子网掩码。默认情况下&#xff0c;DHCP作为Windows Se…

在SPDK中体验一下E810网卡ADQ直通车

早在2019年&#xff0c;Intel发布第二代Xeon Scalable系列处理器的同时&#xff0c;也推出了E800系列网卡。该网卡的亮点除了支持100Gb&#xff0c;便是新增了ADQ功能。1. 了解ADQADQ 全称Application Device Queues&#xff0c;是一种队列和控制技术&#xff0c;可提高应用程序…

44. 含并行连结的网络(GoogLeNet)

GoogLeNet吸收了NiN中串联网络的思想&#xff0c;并在此基础上做了改进。 这篇论文的一个重点是解决了什么样大小的卷积核最合适的问题。 毕竟&#xff0c;以前流行的网络使用小到1 * 1&#xff0c;大到11 * 11的卷积核。 本文的一个观点是&#xff0c;有时使用不同大小的卷积…