LinkedList与链表

news2025/2/27 11:26:10

目录

1.链表

2.链表的模拟实现

3.LinkedList的模拟实现

4.LinkedList的使用

4.1 什么是LinkedList

4.2 LinkedList的使用

5.ArrayList和LinkedList的区别


我的GitHub:Powerveil · GitHub

我的Gitee:Powercs12 (powercs12) - Gitee.com

皮卡丘每天学Java

1.链表

链表也是线性表的一种。

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

注意:

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

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

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

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

1. 单向或者双向

2. 带头或者不带头

3. 循环或者非循环 

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

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

2.链表的模拟实现

public class MyLinkedList {

    public ListNode head;

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

    /**
     * 通过穷举的方式 创建一个链表出来
     * 现在这样做  只是为了能够让初学者对这个结构更好的了解
     * 后期 我会改回来。
     */
    public void createList() {
        ListNode node1 = new ListNode(10);
        ListNode node2 = new ListNode(12);
        ListNode node3 = new ListNode(23);
        ListNode node4 = new ListNode(34);
        node1.next = node2;
        node2.next = node3;
        node3.next = node4;
        head = node1;
    }


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

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

    //查找是否包含关键字key是否在单链表中
    public boolean contains(int key) {
        ListNode cur = head;
        while (cur != null) {
            if (cur.val == key) return true;
            cur = cur.next;
        }
        return false;
    }


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



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

    //尾插法:O(n)
    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;
        }
    }


    private void checkIndex(int index) {
        if (index < 0 || index > size()) {
            throw new IndexNotLegalException("index的值不合法");
        }
    }


    //任意位置插入,第一个数据节点0号下标
    public void addIndex(int index, int data) {
        checkIndex(index);
        //下标为0是头插
        if (index == 0) {
            addFirst(data);
        }
        //下标为size是尾插
        if (index == size()) {
            addLast(data);
        }
        //中间插
        ListNode node = new ListNode(data);
        ListNode cur = findIndex(index - 1);
        node.next = cur.next;
        cur.next = node;
    }

    //找到指定下标节点
    public ListNode findIndex(int index) {
        ListNode cur = head;
        while (index > 0) {
            cur = cur.next;
            index--;
        }
        return cur;
    }

    //删除第一次出现关键字为key的节点
    public void remove(int key) {
//        if (head == null) {
//            throw new RuntimeException("链表没有元素不可以删除");
//        }
        if (head.val == key) {
            head = head.next;
            return;
        }

        ListNode cur = searchPrevOfKey(key);
        if (cur == null) return;
        ListNode del = cur.next;
        cur.next = del.next;
//        cur.next = cur.next.next;
    }


    //根据值找到前一个节点
    private ListNode searchPrevOfKey(int key) {
        if (head == null) return null;

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


    //删除所有值为key的节点
    public void removeAllKey(int key) {
        if (head == null) return;
        ListNode cur = head;
        while (cur.next != null) {
            ListNode curNext = cur.next;
            if (curNext.val == key) {
                cur.next = curNext.next;
            } else {
                cur = curNext;
            }
        }
        if (head.val == key) {
            head = head.next;
        }
    }



    //清空链表
    public void clear() {
        ListNode cur = head;
        while (cur != null) {
            ListNode curNext = cur.next;
            cur.next = null;
            cur = curNext;
        }
        head = null;
    }

}

3.LinkedList的模拟实现

public class MyLinkedList {
    // 头节点
    public ListNode head;
    // 节点
    static class ListNode {
        public int val;
        public ListNode next;
        public ListNode(int val) {
            this.val = val;
        }
    }

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

    // 从某个节点开始打印
    public void display(ListNode node) {
        ListNode cur = node;
        while (cur != null) {
            System.out.printf(cur.val + " ");
            cur = cur.next;
        }
        System.out.println();
    }

    // 查找是否包含关键字key是否在单链表中
    public boolean contains(int key) {
        ListNode cur = head;
        while (cur != null) {
            if (cur.val == key) return true;
            cur = cur.next;
        }
        return false;
    }


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



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

    // 尾插法:O(n)
    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;
        }
    }

    // 检查下标是否合法
    private void checkIndex(int index) {
        if (index < 0 || index > size()) {
            throw new IndexNotLegalException("index的值不合法");
        }
    }


    // 任意位置插入,第一个数据节点0号下标
    public void addIndex(int index, int data) {
        checkIndex(index);
        // 下标为0是头插
        if (index == 0) {
            addFirst(data);
        }
        // 标为size是尾插
        if (index == size()) {
            addLast(data);
        }
        // 中间插
        ListNode node = new ListNode(data);
        ListNode cur = findIndex(index - 1);
        node.next = cur.next;
        cur.next = node;
    }

    // 找到指定下标节点
    public ListNode findIndex(int index) {
        ListNode cur = head;
        while (index > 0) {
            cur = cur.next;
            index--;
        }
        return cur;
    }

    // 删除第一次出现关键字为key的节点
    public void remove(int key) {
//        if (head == null) {
//            throw new RuntimeException("链表没有元素不可以删除");
//        }
        if (head.val == key) {
            head = head.next;
            return;
        }

        ListNode cur = searchPrevOfKey(key);
        if (cur == null) return;
        ListNode del = cur.next;
        cur.next = del.next;
//        cur.next = cur.next.next;
    }


    // 根据值找到前一个节点
    private ListNode searchPrevOfKey(int key) {
        if (head == null) return null;

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


    // 删除所有值为key的节点
    public void removeAllKey(int key) {
        if (head == null) return;
        ListNode cur = head;
        while (cur.next != null) {
            ListNode curNext = cur.next;
            if (curNext.val == key) {
                cur.next = curNext.next;
            } else {
                cur = curNext;
            }
        }
        if (head.val == key) {
            head = head.next;
        }
    }


    // 清空链表
    public void clear() {
        ListNode cur = head;
        while (cur != null) {
            ListNode curNext = cur.next;
            cur.next = null;
            cur = curNext;
        }
        head = null;
    }
}

4.LinkedList的使用

4.1 什么是LinkedList

官方文档

LinkedList (Java Platform SE 8 )

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

 下面是类图

 

1. LinkedList实现了List接口
2. LinkedList的底层使用了双向链表
3. LinkedList没有实现RandomAccess接口,因此LinkedList不支持随机访问

注意:插入和删除的时间复杂度都为O(n),因为找到节点需要遍历

4.2 LinkedList的使用

LinkedList的构造

方法解释
LinkedList()无参构造
public LinkedList(Collection<? extends E> c)使用其他集合容器中元素构造List
@SuppressWarnings({"all"})
public class Test {
    public static void main(String[] args) {
        // 构造一个空的LinkedList
        LinkedList<String> linkedList1 = new LinkedList<>();
        List<String> list = new ArrayList<>();

        list.add("Hello world!");
        list.add("每天学Java");
        list.add("逐渐提升");
        list.add("...");
        // 使用ArrayList构造LinkedList
        LinkedList<String> linkedList2 = new LinkedList<>(list);

    }
}

LinkedList的其他常用方法介绍

方法解释
boolean add(E e)尾插 e
void add(int index, E element)将 e 插入到 index 位置
boolean addAll(Collection<? extends E> c)尾插 c 中的元素
E remove(int index)删除 index 位置元素
boolean remove(Object o)删除遇到的第一个 o
E get(int index)获取下标 index 位置元素
E set(int index, E element)将下标 index 位置元素设置为 element
void clear()清空
boolean contains(Object o)判断 o 是否在线性表中
int indexOf(Object o)返回第一个 o 所在下标
int lastIndexOf(Object o)返回最后一个 o 的下标
List<E> subList(int fromIndex, int toIndex)截取部分 list
    public static void main(String[] args) {
        LinkedList<Integer> list = new LinkedList<>();
        list.add(1); // add(elem): 表示尾插
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        list.add(6);
        list.add(7);
        System.out.println(list.size());
        System.out.println(list);
        // 在起始位置插入0
        list.add(0, 0); // add(index, elem): 在index位置插入元素elem
        System.out.println(list);
        list.remove(); // remove(): 删除第一个元素,内部调用的是removeFirst()
        list.removeFirst(); // removeFirst(): 删除第一个元素
        list.removeLast(); // removeLast(): 删除最后元素
        list.remove(1); // remove(index): 删除index位置的元素
        System.out.println(list);
        // contains(elem): 检测elem元素是否存在,如果存在返回true,否则返回false
        if (!list.contains(1)) {
            list.add(0, 1);
        }
        list.add(1);
        System.out.println(list);
        System.out.println(list.indexOf(1)); // indexOf(elem): 从前往后找到第一个elem的位置
        System.out.println(list.lastIndexOf(1)); // lastIndexOf(elem): 从后往前找第一个1的位置
        int elem = list.get(0); // get(index): 获取指定位置元素
        list.set(0, 100); // set(index, elem): 将index位置的元素设置为elem
        System.out.println(list);
        // subList(from, to): 用list中[from, to)之间的元素构造一个新的LinkedList返回
        List<Integer> copy = list.subList(0, 3);
        System.out.println(list);
        System.out.println(copy);
        list.clear(); // 将list中元素清空
        System.out.println(list.size());
    }

LinkedList的遍历

    public static void main(String[] args) {
        LinkedList<Integer> list = new LinkedList<>();
        list.add(1); // add(elem): 表示尾插
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        list.add(6);
        list.add(7);
        System.out.println(list.size());
        // foreach遍历
        for (int e:list) {
            System.out.print(e + " ");
        }
        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() +" ");
        }
        System.out.println();
    }

5.ArrayList和LinkedList的区别

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

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

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

相关文章

机器人十大前沿热点领域(2022-2023)

中国电子学会嵌入式系统与机器人分会主任委员、北京航空航天大学机器人研究所名誉所长、中关村智友研究院院长王田苗博士出席大会闭幕式&#xff0c;并现场发布《机器人十大前沿热点领域(2022-2023)》。归纳出2022-2023年机器人十大前沿技术&#xff1b;并结合我国国情和机器人…

自动化项目倍加福测距仪QSM WCS RS485 与西门子S7 200通信

1、程序流程图 2、WCS位置数据处理流程 第一步&#xff1a;设置S7-200的RS485的通讯波特率19.2kbps&#xff0c;通讯格式&#xff08;8&#xff0c;1&#xff0c;E&#xff09;&#xff1b; 第二步&#xff1a;PLC向WCS发送请求码&#xff1a; A0A1为0&#xff0c;表示读码器地…

现代密码学导论-16-选择明文攻击和CPA安全

目录 PCA不可区分实验 DEFINITION 3.21 PCA安全的加密方案 LR预言机实验 DEFINITION 3.22 多明文PCA安全的加密方案 THEOREM 3.23 定义3.21和定义3.22等价 PCA不可区分实验 通过运行G(1^n)获得密钥k敌手A被给定输入1^n并拥有访问预言机Enck()的权利&#xff0c;敌手A输出一…

想要精通算法和SQL的成长之路 - 无重叠区间

想要精通算法和SQL的成长之路 - 无重叠区间前言一. 无重叠区间前言 想要精通算法和SQL的成长之路 - 系列导航 一. 无重叠区间 原题链接 给定一个区间的集合 intervals &#xff0c;其中 intervals[i] [starti, endi] 。返回 需要移除区间的最小数量&#xff0c;使剩余区间互…

神经网络和深度学习-处理多维特征的输入

处理多维特征的输入 前面有两个数据集&#xff0c;一个回归&#xff0c;一个分类。 在回归中输出y属于实数&#xff0c;而在分类中输出y属于一个离散的集合 例如在糖尿病分类的数据集中Diabetes Dataset&#xff0c;每一行作为一个sample&#xff08;样本&#xff09;&#x…

Node.js 入门教程 11 Node.js 从命令行接收参数

Node.js 入门教程 Node.js官方入门教程 Node.js中文网 本文仅用于学习记录&#xff0c;不存在任何商业用途&#xff0c;如侵删 文章目录Node.js 入门教程11 Node.js 从命令行接收参数11 Node.js 从命令行接收参数 当使用以下命令调用 Node.js 应用程序时&#xff0c;可以传入任…

Android -- 每日一问:两个 Activity 之间如何传递参数?

经典回答 使用 Intent 的 Bundle 协带参数&#xff0c;就是我们常用的 Intent.putExtra 方法。 除了传递基本类型外&#xff0c;如何传递自定义的对象呢&#xff1f; 这个问题就是想引出 Android 的 Parcelable 。一般很多面试者都有用过传递实现了Serializable接口的自定义对…

JavaWeb学习-监听器

什么是监听器? 类似于前端的事件绑定,java中的监听器用于监听web应用中某些对象、信息的创建、销毁、增加&#xff0c;修改&#xff0c;删除等动作的发生&#xff0c;然后作出相应的响应处理。当范围对象的状态发生变化的时候&#xff0c;服务器自动调用监听器对象中的方法。…

Flutter高仿微信-第54篇-群聊-邀请好友

Flutter高仿微信系列共59篇&#xff0c;从Flutter客户端、Kotlin客户端、Web服务器、数据库表结构、Xmpp即时通讯服务器、视频通话服务器、腾讯云服务器全面讲解。 详情请查看 效果图&#xff1a; 实现代码&#xff1a; import package:flutter/material.dart; import package…

【JavaEE】HTML

努力经营当下&#xff0c;直至未来明朗&#xff01; 文章目录前言一、HTML概述二、【HTML常见标签】1. 注释标签2. 标题标签&#xff1a;h1-h63. 段落标签&#xff1a;p4.换行标签&#xff1a;br5.【格式化标签】5. 图片标签&#xff1a;img 【单标签&#xff0c;没有结束标签】…

HTML+CSS简单漫画网页设计成品 蜡笔小新3页 大学生个人HTML网页制作作品

HTML实例网页代码, 本实例适合于初学HTML的同学。该实例里面有设置了css的样式设置&#xff0c;有div的样式格局&#xff0c;这个实例比较全面&#xff0c;有助于同学的学习,本文将介绍如何通过从头开始设计个人网站并将其转换为代码的过程来实践设计。 ⚽精彩专栏推荐&#x1…

GreenPlum AOCO列存读IO原理

GreenPlum AOCO列存读IO原理GP自带AOCO列存&#xff0c;它的IO和heap表的IO是分开的。Heap表的脏数据由checkpoint或后台write进程刷写&#xff0c;也就是FlushBuffer函数调用smgrwrite。由magnetic disk storage manager管理IO模块。但是AOCO列存则是由本身进行管理&#xff0…

【信息融合】BP神经网络和DS证据理论不确定性信息融合问题【含Matlab源码 2204期】

⛄一、 D-S证据理论及解释 证据理论由Dempster在1967年最初提出,并由他的学生Shafer改进推广使之成为符合有限离散领域中推理的形式,因此称为D-S理论。证据理论讨论一个“辨识框架”(Frame of Discernment)Θ,它是关于命题的相互独立的可能答案或假设的一个有限集合。按传统方…

基于POI的可快速定制Excel导出脚本设想

基于POI的可快速定制Excel导出脚本设想 背景 年关将至&#xff0c;业务人员提出了好多比较着急但是又不常用的取数需求。所谓不常用&#xff0c;大概了是由于业务人员前期调研产生的临时需求&#xff0c;后续也不会大面积铺开&#xff0c;没必要专门分配人员去开发。所谓比较着…

诊断数据库ODX—数据库框架(基于ISO22901详解)

文章目录 前言一、ODX数据库自身架构是什么&#xff1f;二、ODX数据库架构具体组成部分和含义总结前言 车载诊断现阶段应用的诊断数据库大体分为三种&#xff1a; CDD&#xff08;Vector私有格式&#xff09;&#xff1b; ODX全球通用诊断数据库格式&#xff1b; DEXT&…

【零基础入门SpringMVC】第四期——RESTFUL专题

一、RESTFul 概述 1、什么是 RESTFul&#xff1f; REST 全称 Representational State Transfer 代表 表现层资源状态转移 视图层 控制层 表现层 百度百科这样说&#xff1a; RESTFUL是一种网络应用程序的设计风格和开发方式&#xff0c;基于HTTP&#xff0c;可以使用XML格式…

轻松应对80% 的工作场景?GitHub 爆赞的 Java 高并发与集合框架,面试官也拿我没辙

在工作中&#xff0c;笔者经常和掌握不同技术的朋友讨论具体问题的解决方案&#xff0c;发现在 Java 体系中&#xff0c;大家使用最多的是 Java 集合框架&#xff08;JCF&#xff09;和 Java 并发工具包&#xff08;JUC&#xff09;。实际上&#xff0c;JCF 和 JUC 已经能够覆盖…

Flutter高仿微信-第50篇-群聊-查看群成员

Flutter高仿微信系列共59篇&#xff0c;从Flutter客户端、Kotlin客户端、Web服务器、数据库表结构、Xmpp即时通讯服务器、视频通话服务器、腾讯云服务器全面讲解。 详情请查看 效果图&#xff1a; 实现代码&#xff1a; import package:flutter/material.dart; import package:…

Node.js 入门教程 7 从命令行运行 Node.js 脚本 8 如何退出 Node.js 程序

Node.js 入门教程 Node.js官方入门教程 Node.js中文网 本文仅用于学习记录&#xff0c;不存在任何商业用途&#xff0c;如侵删 文章目录Node.js 入门教程7 从命令行运行 Node.js 脚本8 如何退出 Node.js 程序7 从命令行运行 Node.js 脚本 运行 Node.js 程序的常用方法是&#…