手敲MyLinkedList,简单了解其运行逻辑

news2025/2/26 0:59:51

1.LinkedList的介绍和结构

        LinkedList的底层是双向链表结构,相对于之前的单向无头非循环链表来说,LinkedList最大的区别就是该链表可以增加了一条链接逻辑,可以从最后一个节点通过地址访问来到整个链表的头结点。

        通过以下集合框架,LinkedList也实现了List接口,具体如下:

        注意: 

        1. LinkedList 实现了 List 接口
        2. LinkedList 的底层使用了双向链表
        3. LinkedList 没有实现 RandomAccess 接口,因此 LinkedList 不支持随机访问
        4. LinkedList 的任意位置插入和删除元素时效率比较高,时间复杂度为 O(1)
        5. LinkedList 比较适合任意位置插入的场景

        1.1 LinkedList的结构 

        双向无头非循环链表的节点是由三部分构成的,用来存储数据的value域,存放下一个节点地址的next域,以及用来存放前一个节点地址的prev域,其节点和链表结构如下图所示;

                   

        简单要点如下: 

                   

2.无头双向非循环链表的实现

2.1 自定义MyLinkedList类

        1、建立一个Ilist接口,在里面构造mylinkedlist链表要实现的抽象方法;

public interface IList {
    //头插法
    void addFirst(int data);
    //尾插法
    void addLast(int data);
    //任意位置插入,第一个数据节点为0号下标
    void addIndex(int index,int data);
    //查找是否包含关键字key是否在单链表当中
    boolean contains(int key);
    //删除第一次出现关键字为key的节点
    void remove(int key);
    //删除所有值为key的节点
    void removeAllKey(int key);
    //得到单链表的长度
    int size();
    void clear();
    void display();
}

        无头双向非循环链表的节点是由三个属性(value域、prev域和next域构成的),同时也要在自定义MyLinkedList类里面使用内部类创建链表节点类,之后在链表类里面创建一个头结点head来代表当前链表的引用,同时创建一个节点last来表示当前链表的最后一个节点;同时让该自定义类实现我们之前创建的接口,接下来重写接口里面的方法,让其能够具体化; 

public class MyLinkedList implements IList {
    static class ListNode {
        public int value;
        public ListNode prev;
        public ListNode next;

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

    public ListNode head;
    public ListNode last;

    @Override
    public void addFirst(int data) {

    }

    @Override
    public void addLast(int data) {

    }

    @Override
    public void addIndex(int index, int data) {

    }

    @Override
    public boolean contains(int key) {
        return false;
    }

    @Override
    public void remove(int key) {

    }

    @Override
    public void removeAllKey(int key) {

    }

    @Override
    public int size() {
        return 0;
    }

    @Override
    public void clear() {

    }

    @Override
    public void display() {

    }
}

2.2头插法 

        思路(图解如下):

        1、如果是空链表(头节点head为null),则要添加的节点node就是头节点,也是尾节点.

        2、头节点不为null:

        2.1 将原先head的前驱节点指向新增节点位置,新增节点后驱节点指向head节点的位置,注意和新节点建立连接一定要从前往后建立;

        

        2.2 head指向新增节点位置(新的链表的第一个节点为head)。

@Override
    public void addFirst(int data) {
        ListNode node = new ListNode(data);
        if (head == null){
            head = node;
            last = node;
        }else {
            node.next = head;
            head.prev = node;
            head = node;
        }
    }

         执行结果在2.3中;

2.3 遍历链表

        思路:与mysinglelist链表的相同,这里略;代码如下

@Override
    public void display() {
        ListNode cur = head;
        while (cur != null){
            System.out.print(cur.value+"->");
            cur = cur.next;
        }
        System.out.println(" ");
    }
    public static void main(String[] args) {
        MyLinkedList myLinkedList = new MyLinkedList();
        myLinkedList.addFirst(1);
        myLinkedList.addFirst(0);
//        myLinkedList.addFirst(2);
        myLinkedList.display();

    }

        执行结果如下:

        

2.4尾插法        

        思路:

          1、如果是空链表(头节点head为null),则要添加的节点node就是头节点,也是尾节点.

          2、头节点不为null:

          2.1 将原先last的后驱节点(last.prev)指向新增节点(node)位置,新增节点前驱节点指向last节点的位置,注意和新节点建立连接一定要从前往后建立

        2,2将node节点改为新的last节点;

        代码和测试结果如下:

@Override
    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;
        }
    }
public static void main(String[] args) {
        MyLinkedList myLinkedList = new MyLinkedList();
        myLinkedList.addFirst(1);
        myLinkedList.addFirst(0);
        myLinkedList.display();
        myLinkedList.addLast(7);
        myLinkedList.addLast(8);
        myLinkedList.display();
    }

                    

2.5 链表长度

        对整个链表进行遍历,使用计数器进行记录遍历的次数,最后将计数器的值返回即可,下图代码是该方法的具体实现;

 @Override
    public int size() {
        int count = 0;
        ListNode cur = head;
        while (cur != null){
            count++;
            cur=cur.next;
        }
        return count;
    }
public static void main(String[] args) {
        MyLinkedList myLinkedList = new MyLinkedList();
        myLinkedList.addFirst(1);
        myLinkedList.addFirst(0);
        myLinkedList.display();
        myLinkedList.addLast(7);
        myLinkedList.addLast(8);
        myLinkedList.display();
        System.out.println(myLinkedList.size());
    }

        测试结果如下:

        

2.6 任意位置插入

 思路:

        1、需要插入的位置必须为合法,如果不合法,我们会抛出一个异常进行提醒,所以首先自定义一个异常;

public class ListIndexOutOfException extends RuntimeException{
    public ListIndexOutOfException() {
    }
 
    public ListIndexOutOfException(String message) {
        super(message);
    }
}

         2、任意位置插入,首先分几种情况,插在开头,插在结尾,插在中间

        2.1 当插在链表开头和结尾时,可以使用头插法和尾差法

        2.2 如下图所示,按照z字形进行赋值和写代码;当插在其他的位置时,首先让cur走到index的位置(此处创建一个方法,找到index位置的节点并将这个节点定义为cur返回)(这时候就需要考虑将下一个节点加在index的位置时如何处理建立连接的顺序);其次注意建立连接的时候,一定要先建立原index前的节点和node节点添加节点的连接,其次再建立添加节点(node)和原index节点的连接,链表图解如下:

         具体方法代码如下:

@Override
    public void addIndex(int index, int data) {
        ListNode node = new ListNode(data);
        if(index < 0 || index > size()) {
            //抛自定义的异常
            throw new ListIndexOutOfException("你当前输入的索引有问题");
        }
        if(index == 0) {
            addFirst(data);
            return;
        }
        if(index == size()) {
            addLast(data);
            return;
        }
        ListNode cur = findIndex(index);
        node.next = cur;
        cur.prev.next = node;
        node.prev = cur.prev;
        cur.prev = node;
    }

    private ListNode findIndex(int index) {
        ListNode cur = head;
        while (index != 0){
            index--;
            cur = cur.next;
        }
        return cur;
    }
public static void main(String[] args) {
        MyLinkedList myLinkedList = new MyLinkedList();
        myLinkedList.addFirst(1);
        myLinkedList.addFirst(0);
        myLinkedList.display();
        myLinkedList.addLast(7);
        myLinkedList.addLast(8);
        myLinkedList.display();
        System.out.println(myLinkedList.size());
        myLinkedList.addIndex(3,99);
        myLinkedList.display();
    }

2.7 查找关键字

        对链表进行遍历,然后将关键字key和链表数值进行比较,如果存在key关键字则返回true;反之则返回false;

        方法具体实现的代码如下:

 @Override
    public boolean contains(int key) {
        ListNode cur = head;
        while (cur != null) {
            if (cur.value == key) {
                return true;
            }
            cur = cur.next;
        }
        return false;
    } 
public static void main(String[] args) {
        MyLinkedList myLinkedList = new MyLinkedList();
        myLinkedList.addFirst(1);
        myLinkedList.addFirst(0);
        myLinkedList.addLast(7);
        myLinkedList.addLast(8);
        myLinkedList.addIndex(3,99);
        myLinkedList.display();
        System.out.println(myLinkedList.contains(1));
    }

         测试代码和执行结果如下:

2.8删除第一个关键字为key的节点

        思路:

        1、链表为空链表,不用操作;
        2、删除数据在第一个:首先让cur节点移动到第二个节点,其次判断新的链表是否只有一个节点

        2.1 如果只剩下cur这个节点,则让head指向null,让last指向null

        2.2让cur这个节点的prev指向null,其他的不变


        3、没有你要删除的数据,不用操作
        4、有你要删除的数据且不是第一个(cur节点是要删除的节点)

        4.1 删除数据最后一个:让last节点往前移一个单位

        4.2删除的数据不是最后一个:首先让cur的前一个节点的next域直接存cur下一个节点的地址,其次让cur下一个节点的prev域存放cur前一个节点的地址

        代码如下:

    @Override
    public void remove(int key) {
        ListNode cur = head;
        while (cur != null) {
            if (cur.value == key){
                if(cur == head) {
                    //整个链表的头结点为要删除的节点
                    head = head.next;//head == null
                    if(head == null) {
                        last = null;
                    }else {
                        head.prev = null;
                    }
                }else {
                    //链表的其他节点是要删除的
                    cur.prev.next = cur.next;
                    if(cur.next == null) {
                        //要删除的是最后一个节点
                        last = last.prev;
                    }else {
                        cur.next.prev = cur.prev;
                    }
                }
                return;
            }else {
                cur =cur.next;
            }
        }
    }

2.9删除所有值为key的节点

        与删除第一次出现关键字为key的节点几乎是一模一样的;我们只需要遍历完整个链表,将return删掉就好。

        代码如下:

 @Override
    public void removeAllKey(int key) {
        ListNode cur = head;
        while (cur != null) {
            if (cur.value == key){
                if(cur == head) {
                    //整个链表的头结点为要删除的节点
                    head = head.next;//head == null
                    if(head == null) {
                        last = null;
                    }else {
                        head.prev = null;
                    }
                }else {
                    //链表的其他节点是要删除的
                    cur.prev.next = cur.next;
                    if(cur.next == null) {
                        //要删除的是最后一个节点
                        last = last.prev;
                    }else {
                        cur.next.prev = cur.prev;
                    }
                }
            }
                cur =cur.next;
        }
    }

2.10清空链表

        1、只需要遍历整个链表,将每个节点的前驱与后继节点都置为null就好

    public void clear(){
        ListNode cur = head;
        while(cur != null) {
            cur.prev  = null;
            cur = cur.next;
            cur.prev.next = null;
        }
    }
 

         2、将链表的首节点和为节点指向null即可;

 public void clear() {
        head = null;
        last = null;
    }

 ps:本次内容就到这里了,如果你喜欢的话就请一键三连!!!

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

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

相关文章

【数据库】数据库多种锁模式,共享锁、排它锁,更新锁,增量锁,死锁消除与性能优化

多种锁模式的封锁系统 ​专栏内容&#xff1a; 手写数据库toadb 本专栏主要介绍如何从零开发&#xff0c;开发的步骤&#xff0c;以及开发过程中的涉及的原理&#xff0c;遇到的问题等&#xff0c;让大家能跟上并且可以一起开发&#xff0c;让每个需要的人成为参与者。 本专栏会…

判断三角形-第11届蓝桥杯选拔赛Python真题精选

[导读]&#xff1a;超平老师的Scratch蓝桥杯真题解读系列在推出之后&#xff0c;受到了广大老师和家长的好评&#xff0c;非常感谢各位的认可和厚爱。作为回馈&#xff0c;超平老师计划推出《Python蓝桥杯真题解析100讲》&#xff0c;这是解读系列的第12讲。 判断三角形&#…

基于景区智慧灯杆、智能指路牌基础设施的景区建设应用

智慧景区是指运用现代信息技术手段&#xff0c;将景区内的资源、服务、管理等进行数字化、网络化和智能化整合&#xff0c;打造出高效便捷、安全舒适、互动体验和可持续发展的景区。智慧景区可以从以下几个方面进行体现&#xff1a; 智慧导览&#xff1a;通过使用智能化的导览…

二叉树OJ题目——C语言

LeetCode 104.二叉树的最大深度 1. 题目描述&#xff1a; 给定一个二叉树 root &#xff0c;返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;3示例…

Ubuntu 安装 MySQL8 配置、授权、备份、远程连接

目录 0100 系统环境0200 下载0300 安装0400 服务管理0401 关闭、启动、重启服务0402 查看服务状态 0500 查看配置文件0600 账号管理0601 添加账号0602 删除账号0603 修改密码0604 忘记root密码 0700 自动备份0800 远程访问 0100 系统环境 [rootlocalhost ~]# cat /proc/versio…

canvas基础:绘制虚线

canvas实例应用100 专栏提供canvas的基础知识&#xff0c;高级动画&#xff0c;相关应用扩展等信息。 canvas作为html的一部分&#xff0c;是图像图标地图可视化的一个重要的基础&#xff0c;学好了canvas&#xff0c;在其他的一些应用上将会起到非常重要的帮助。 文章目录 示例…

rtmp 协议详解

1. handshake 1.1 概述 rtmp 连接从握手开始。它包含三个固定大小的块。客户端发送的三个块命名为 C0,C1,C2&#xff1b;服务端发送的三个块命名为 S0,S1,S2。 握手序列&#xff1a; 客户端通过发送 C0 和 C1 消息来启动握手过程。客户端必须接收到 S1 消息&#xff0c;然后…

使用 Go 构建高性能的命令行工具

命令行工具&#xff08;CLI&#xff09;在软件开发中扮演着重要的角色&#xff0c;尤其是在自动化工具、开发工具链和服务器管理等领域。Go 语言以其简洁性和高性能而闻名&#xff0c;非常适合用来创建强大且高效的 CLI 工具。本文将详细介绍如何使用 Go 语言来构建 CLI 应用&a…

【c++中的四种类型转换,应用场景】

c中的四种类型转换 1.静态转换 &#xff1a; static_cast 用法&#xff1a;static_cast<type_name>(val) 1.基本类型间的转换 enum Day { Mon 1, Tues 2, Wed 3, Thu 4, Fir 5, Sat 6, Sun 7 };int main() {int a 10;char ch t;double dx 21.65;a static_…

来CSDN一周年啦!!!

各位CSDN的uu们你们好呀&#xff0c;今天是小雅兰来到CSDN创作的一周年啦&#xff0c;时间&#xff0c;说长不长&#xff0c;说短也不短&#xff0c;在这一年中&#xff0c;我认为我也收获了一些很有价值的东西吧&#xff01;&#xff01; 一周年了&#xff0c;该创作的还得继续…

正则表达式(基础、常用)

正则&#xff08;RegExp&#xff09;:用于检测字符串是否符合该规则&#xff0c;符合返回值为true,不符合返回值为false 一、定义正则表达式 1、字面量方式 const reg/a/ // 字符串中含有a即可 2、构造函数方式 const reg0new RegExp(a)二、元字符(特殊字符) 1、\d :匹…

1998-2021年全国各区县PM2.5平均浓度数据

1998-2021年全国各区县PM2.5平均浓度数据 1、时间&#xff1a;1998-2021年 2、指标&#xff1a;省、省代码、市、市代码、县代码、县、年份、均值、总和、最小值、最大值、标准差 3、来源&#xff1a;Washington university Atmospheric Composition Analysis Group 4、范围…

【Linux】-信号-(信号的产生,保存,处理,以及os是怎么读取硬件的输入,硬件异常和coredump,定时器的原理简单的用户态和内核态的详细介绍)

&#x1f496;作者&#xff1a;小树苗渴望变成参天大树&#x1f388; &#x1f389;作者宣言&#xff1a;认真写好每一篇博客&#x1f4a4; &#x1f38a;作者gitee:gitee✨ &#x1f49e;作者专栏&#xff1a;C语言,数据结构初阶,Linux,C 动态规划算法&#x1f384; 如 果 你 …

SpringBoot-Vue项目初始搭建

SpringBoot-Vue项目初始搭建 1、项目搭建 前提&#xff1a;配置过nodejs环境&#xff0c;安装了vuecli&#xff08;如果未配置&#xff0c;可以参照此教程&#xff1a;https://www.bilibili.com/video/BV18E411a7mC/ p12&#xff09; 新建文件夹(最好不要有中文) 打开cmd …

AirServer怎么用?如何AirServer进行手机投屏

什么是 AirServer&#xff1f; AirServer 是适用于 Mac 和 PC 的先进的屏幕镜像接收器。 它允许您接收 AirPlay 和 Google Cast 流&#xff0c;类似于 Apple TV 或 Chromecast 设备。AirServer 可以将一个简单的大屏幕或投影仪变成一个通用的屏幕镜像接收器 &#xff0c;是一款…

C语言实现猜数字游戏

前面我们已经了解了分支循环、数据类型及变量的知识点&#xff0c;今天我将用之前学过的知识进行实操&#xff0c;将所学的知识进行巩固和提升。下面的讲解仅我个人认知水平&#xff0c;如有欠缺之处&#xff0c;欢迎大家指正&#xff0c;并且我希望初学者在看完讲解后可以独立…

汇编语言实现音乐播放器

目标程序 用汇编语言实现一个音乐播放器&#xff0c;并支持点歌 Overview 乐曲是按照一定的高低、长短和强弱关系组成的音调&#xff0c;在一首乐曲中&#xff0c;每个音符的音高和音长与频率和节拍有关&#xff0c;因此我们要分别为3首要演奏的乐曲定义一个频率表和一个节拍…

生成带依赖Jar 包的两种常用方式:IDEA打包工具:Artifacts 和 maven-shade-plugin

文章目录 前言1、IDEA打包工具&#xff1a;Artifacts1.1 创建Artifacts1.2 选择第三方jar文件1.3 打包Artifacts1.4 测试jar包 2、maven-shade-plugin2.1、pom文件添加2.2、打包2.3、测试jar包 总结 前言 当我们编写完Java程序后&#xff0c;为了提高执行效率通常会将应用程序…

MySQL5.7安装与配置:自动化一键安装配置

介绍 本文介绍了一个自动化安装MySQL的Shell脚本。该脚本可以帮助用户快速安装MySQL&#xff0c;并自动进行配置和初始化。通过使用该脚本&#xff0c;用户无需手动执行繁琐的安装步骤&#xff0c;大大简化了MySQL的安装过程。 使用shell自动化安装教程 1. 复制脚本 首先&a…