【数据结构】链表(一)

news2024/10/7 6:44:20

链表(一)

文章目录

  • 链表(一)
    • 01 引入
    • 02 概念及结构
    • 03 单向不带头不循环链表实现
      • 3.1 创建节点类型
      • 3.2 简易创建一个链表
      • 3.3 遍历链表每个节点
      • 3.4 获取链表长度
      • 3.5 查找是否包含关键字key是否在单链表当中
      • 3.6 头插法
      • 3.7 尾插法
      • 3.8 任意位置插入
      • 3.9 删除第一次出现关键字为key的节点
      • 3.10 删除所有值为key的节点
      • 3.11 清空

01 引入

接上文顺序表,我们可以知道ArrayList的缺陷。当在ArrayList任意位置插入或者删除元素时,就需要将后序元素整体往前或者往后搬移,时间复杂度为O(n),效率比较低,因此ArrayList不适合做任意位置插入和删除比较多的场景。因此:java集合中又引入了LinkedList,即链表结构。

那么下面,让我们来了解了解链表。

02 概念及结构

链表是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的 。其实就是节点组成,一个节点类似于火车一个车厢那样:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nxFKWdPq-1691313293619)(C:\Users\86159\AppData\Roaming\Typora\typora-user-images\image-20230802182622237.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NmTz2djR-1691313293621)(E:\Typora_note\数据结构笔记\Java\链表.assets\image-20230805165401086.png)]

下面是一个单向带头非循环链表:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cIeoB8dK-1691313293621)(C:\Users\86159\AppData\Roaming\Typora\typora-user-images\image-20230803142840530.png)]

下面是链表的种类:

单向双向
不带头带头
非循环循环

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kxeLdsML-1691313293622)(https://gitee.com/liuhb-clanguage/picture/raw/master/png/image-20230803144159308.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Yb7FYu2f-1691313293622)(https://gitee.com/liuhb-clanguage/picture/raw/master/png/image-20230803144412289.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PCab9kv4-1691313293623)(C:\Users\86159\AppData\Roaming\Typora\typora-user-images\image-20230803144625610.png)]

通过彼此间的排列组合,一共可以分为:8种,如下:

  1. 单向 不带头 非循环
  2. 单向 不带头 循环
  3. 单向 带头 非循环
  4. 单向 带头 循环
  5. 双向 不带头 非循环
  6. 双向 不带头 循环
  7. 双向 带头 非循环
  8. 双向 带头 循环

这里着重介绍单向 不带头 非循环 和 双向 不带头 非循环。

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

03 单向不带头不循环链表实现

3.1 创建节点类型

链表是由一个一个节点组成的,每一个节点对应每一个对象,如果我们去抽象他,他其实有两个域值,所以我们可以把节点定义成内部类。

创建一个ListNode类来作为节点类型,包含两个成员变量:val域用来储存数据(数据域),next用来存储下一个节点的地址(指针域)。
再创建一个带参的构造方法来实例化对象,同时给val赋值,next的默认值是null。接下来我们用代码来实现一下:

//静态内部类
    static class ListNode{
        public int val; //节点的值域
        public ListNode next; //下一个节点的地址

        //实例化节点对象
        public ListNode(int val){
            this.val = val;
        }
    }

3.2 简易创建一个链表

public ListNode head;//表示当前链表的头节点

    //以穷举的方式创建一个链表
    public void createList(){
        ListNode node1 = new ListNode(12);
        ListNode node2 = new ListNode(23);
        ListNode node3 = new ListNode(34);
        ListNode node4 = new ListNode(45);
        ListNode node5 = new ListNode(56);
    }

此时创建的链表如图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a22rDdR2-1691313293623)(E:\Typora_note\数据结构笔记\Java\链表.assets\image-20230803161957003.png)]

目前各节点间不存在关系。

那么接下来的操作就是要让node1->node2->node3->node4->node5

//以穷举的方式创建一个链表
    public void createList(){
        ListNode node1 = new ListNode(12);
        ListNode node2 = new ListNode(23);
        ListNode node3 = new ListNode(34);
        ListNode node4 = new ListNode(45);
        ListNode node5 = new ListNode(56);
        
        node1.next = node2;
        node2.next = node3;
        node3.next = node4;
        node4.next = node5;
        
        this.head = node1;
    }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KSoJaQPS-1691313293623)(E:\Typora_note\数据结构笔记\Java\链表.assets\image-20230803163008316.png)]

这里我们不妨在编译器里debug来看看:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0gyacBuy-1691313293624)(E:\Typora_note\数据结构笔记\Java\链表.assets\image-20230803221846684.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r9st8GkK-1691313293624)(E:\Typora_note\数据结构笔记\Java\链表.assets\image-20230803221913786.png)]

3.3 遍历链表每个节点

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

这里我们可以在测试类中debug一下看看:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mvZIpdf9-1691313293624)(E:\Typora_note\数据结构笔记\Java\链表.assets\image-20230803225415004.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zQCGDYFR-1691313293625)(E:\Typora_note\数据结构笔记\Java\链表.assets\image-20230803225425896.png)]

虽然这里的确是将链表遍历完全,但是,这里的头节点head = null
在这里插入图片描述

那么这样就会造成一个后果,鬼都不知道头节点head死哪里去了,那咋办?

这个后果虽然很严重,但是解决办法其实也很容易,既然head它不能乱来,那它可以叫个分身cur代替它去呀。

public void display() {
        ListNode cur = head;
        while (cur != null){
            //如果cur == null,说明把链表遍历完成
            System.out.println(cur.val+" ");
            cur = cur.next;
            //cur每次向后走一步
        }
        System.out.println();
    }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4tyC9HTA-1691313293625)(E:\Typora_note\数据结构笔记\Java\链表.assets\image-20230803232650482.png)]

3.4 获取链表长度

这个其实也就是基于3.3 遍历链表得来的。

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

3.5 查找是否包含关键字key是否在单链表当中

这个其实也一样是基于3.3 遍历链表得来的。

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

3.6 头插法

头插法指的是在链表的头节点位置插入一个新的节点,定义一个node表示插入的新节点,然后将node.next = head,head = node,即可。

形象一点来看,如图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wq1UPcRp-1691313293625)(E:\Typora_note\数据结构笔记\Java\链表.assets\image-20230804140014328.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uiECf3X7-1691313293626)(E:\Typora_note\数据结构笔记\Java\链表.assets\image-20230804140805784.png)]

//头插法
    public void addFirst(int data){
        ListNode node = new ListNode(data);

        node.next = head;
        head = node;
    }

3.7 尾插法

尾插法指的是:在链表的尾巴节点位置插入一个新节点,定义一个node表示新节点,如同头插法那样,对原尾巴节点的next进行赋值。下面是尾插链表出现的情况:

  • 当链表不为空的时候,定义一个cur来代替head(这里其实和遍历节点的道理一致),直到cur.next == null 的时候就跳出遍历,cur.next == node,这样即可完成尾插。

  • 当链表为空的时候,不论我们定义什么去代替head,都是竹篮打水一场空,都无法进入遍历,同时也会报空指针异常,解决方法其实也很简单,直接让head = node即可。

具体分析见以下:

如图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cQZ0sLBB-1691313293626)(https://gitee.com/liuhb-clanguage/picture/raw/master/png/image-20230804142311057.png)]

其实这其中也有遍历链表那味了,其思想就是遍历到链表最后一个节点,然后再进行尾插就行了。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UcGHqGsq-1691313293626)(https://gitee.com/liuhb-clanguage/picture/raw/master/png/image-20230804143200359.png)]

//尾插法
    public void addLast(int data){
        ListNode node = new ListNode(data);

        ListNode cur = head;
        while (cur.next != null){
            cur = cur.next;
        }
        cur.next = node;
    }

但是这个代码是具有一定的问题的,情景如下图:

在这里插入图片描述

这个问题其实就是,此时head是空的,同时我们定义一个cur= head,那么cur也是空,那么就无法进入到while循环中。修正如下:

//尾插法
    public void addLast(int data){
        ListNode node = new ListNode(data);

        ListNode cur = head;
        if (cur == null){
            head = node;
            return;
        }
        //找到链表的尾巴,注意是cur.next 不是cur
        while (cur.next != null){
            cur = cur.next;
        }
        cur.next = node;
    }

3.8 任意位置插入

思路:

  1. 定义curindex-1步,找到要插入位置的前一个节点
  2. 进行插入

给一个情景,定义cur = index - 1,在1号、2号间插入node.

关键就在于我们要将0x66 -> 0x777,null -> 0x32,即node.next = cur.next,cur.next = node.

需要将head先移至2号位置(注意:用cur代替head,防止head丢失),然后

node.next = cur.next使该节点的next域改为下一节点的地址,再cur.next = node.next使前一节点的next域改为该节点的地址。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ugPmgfIm-1691313293627)(https://gitee.com/liuhb-clanguage/picture/raw/master/png/image-20230804161544727.png)]

//任意位置插入,第一个数据节点为0号下标
    public void addIndex(int index,int data){
        if (index < 0 || index > size()){
            System.out.println("index不合法");
            return;
        }
        if (index == 0){
            addFirst(data);
            return;
        }
        if (index == size()){
            addLast(data);
            return;
        }

        /*ListNode node = new ListNode(data);
        ListNode cur = head;

        int tmp = index - 1;
        while (tmp != 0){
            cur = cur.next;
            tmp--;
        }
        node.next = cur.next;
        cur.next = node;
         */
        //将上面这一坨封装
        ListNode cur = findIndexSubOne(index);
        ListNode node = new ListNode(data);
        node.next = cur.next;
        cur.next = node;
    }

    /**
     * 找到要删除节点位置的前一个节点
     * @param index
     * @return
     */
    private ListNode findIndexSubOne(int index){
        ListNode cur = head;
        int tmp = index - 1;
        while (tmp != 0){
            cur = cur.next;
            tmp--;
        }
        return cur;
    }

3.9 删除第一次出现关键字为key的节点

效果图如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XNjLHKsz-1691313293628)(E:\Typora_note\数据结构笔记\Java\链表.assets\image-20230804163610766.png)]

  • 对于删除第一次出现的key值的节点,若不是头节点,我们只需将key值对应的节点的前一节点的next的域改为key值对应的节点的next域即可。

  • 对于头节点,直接head = head.next即可。

  1. 找到要删除节点的前一个节点
  2. 此时要删除的节点del = cur.next;
  3. 进行删除cur.next = del.next

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3Z4twZk9-1691313293628)(https://gitee.com/liuhb-clanguage/picture/raw/master/png/image-20230804165759876.png)]

//删除第一次出现关键字为key的节点
    public void remove(int key){
        if (head == null){
            return;
        }
        //单独删除头节点
        if (head.val == key){
            head = head.next;
            return;
        }
        
        ListNode cur = searchPrev(key);
        if (cur == null){
            System.out.println("没有你要删除的数字!");
            return;
        }
        ListNode del = cur.next;
        cur.next = del.next;
        
    }

    /**
     * 找到关键字key的前驱
     * @param key
     * @return
     */
    private ListNode searchPrev(int key){
        ListNode cur = head;
        while (cur.next != null){
            if (cur.next.val == key){
                return cur;
            }
            cur = cur.next;
        }
        return null;
    }

3.10 删除所有值为key的节点

情景如下:

将值为23的删除

有种暴力的方法,我们只需要多次调用3.9种的remove函数即可,但是这并不是我们真正想要的,要求:遍历一遍就要删除完。

如图分析:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XnRqeEfk-1691313293628)(https://gitee.com/liuhb-clanguage/picture/raw/master/png/image-20230806160715303.png)]

//删除所有值为key的节点
    public void removeAllKey(int key){
        ListNode prev = head;
        ListNode cur = head.next;

        if (head == null){
            return;
        }

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

3.11 清空

简单粗暴:

public void clear() {
        this.head = null;
    }

温柔:

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

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

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

相关文章

无涯教程-Perl - delete函数

描述 此函数从哈希中删除指定的键和关联的值,或从数组中删除指定的元素。该操作适用于单个元素或切片。 语法 以下是此函数的简单语法- delete LIST返回值 如果键不存在,并且与已删除的哈希键或数组索引关联的值,则此函数返回undef。 Perl 中的 delete函数 - 无涯教程网无…

Java spring boot 全解Camunda 7,从 0 到 1 构建工作流平台——第二节:Spring boot 简单集成

目录 1. 成果展示2. 环境准备3. 项目构建3.1 项目结构3.2 引入Camunda 依赖3.3 启动spring boot 程序3.4 启动 web app 程序 引言&#xff1a;当今技术发展迅猛&#xff0c;企业对于业务流程的高效管理和自动化需求也日益增长。在这个背景下&#xff0c;Spring Boot和Camunda7成…

【网络基础实战之路】基于MGRE多点协议的实战详解

系列文章传送门&#xff1a; 【网络基础实战之路】设计网络划分的实战详解 【网络基础实战之路】一文弄懂TCP的三次握手与四次断开 【网络基础实战之路】基于MGRE多点协议的实战详解 【网络基础实战之路】基于OSPF协议建立两个MGRE网络的实验详解 PS&#xff1a;本要求基于…

Jupyter Notebook 未授权访问远程命令执行漏洞

漏洞描述 Jupyter是一个开源的交互式计算环境&#xff0c;它支持多种编程语言&#xff0c;包括Python、R、Julia等。Jupyter的名称来源于三种编程语言的缩写&#xff1a;Ju(lia)、Py(thon)和R。 Jupyter的主要特点是它以笔记本&#xff08;Notebook&#xff09;的形式组织代码…

Python基础教程——贪吃蛇、连连看小游戏(完整版,附源码)

一、贪吃蛇 1. 案例介绍 贪吃蛇是一款经典的益智游戏&#xff0c;简单又耐玩。该游戏通过控制蛇头方向吃蛋&#xff0c;从而使得蛇变得越来越长。 通过上下左右方向键控制蛇的方向&#xff0c;寻找吃的东西&#xff0c;每吃一口就能得到一定的积分&#xff0c;而且蛇的身子会…

pointpillars在Ubuntu2004训练的总结

1、找到pointpcdet-master之后在此打开终端输入code进入VScode界面 code 2、激活pp环境 conda activate pp 3、cd进入tools cd tools 4、将kitti数据集准备好放入data路径下之后开始训练 python train.py --cfg_file cfgs/kitti_models/pointpillar.yaml 5、训练完成之…

AOSP开发——APN配置文件路径

Android1~9&#xff0c;APN配置文件路径&#xff1a; vendor/sprd/telephony-res/apn/apns-conf_8.xml Android10~12&#xff0c;APN配置文件路径&#xff1a; /vendor/sprd/telephony-res/apn/apns-conf_8_v2.xml Android13&#xff0c;APN配置文件路径&#xff1a; /vendor/…

一文读懂快速开发平台

一、开发平台是什么&#xff1f; 开发平台是指以一或多种编程语言为基础而开发的一种软件&#xff0c;通常其不作为最终的软件产品&#xff0c;它是一类可二次开发的软件框架&#xff0c;开发者能利用其高效地开发各类软件产品。 在利用开发平台进行开发工作时&#xff0c;可摒…

基于Home Assistant远程开门

基于Home Assistant远程开门 1.购买云服务器 1.1 阿里云服务器 本人使用的是阿里云服务器&#xff0c;其他的腾讯云&#xff0c;百度云都可以 如果你想要一个建议的话&#xff1a; 推荐在打折优惠的时候买&#xff0c;比如双十一 阿里云最近有一个飞天计划&#xff0c;在校…

关于丢失安卓秘钥的撞sha-1值的办法

实验得知&#xff0c;安卓sha-1和keytool生成秘钥签名文件的时间有关。 前提条件是&#xff0c;开发者必须知道生成秘钥的所有细节参数 以下是撞文件代码&#xff08;重复生成&#xff09; import time import osidx 0while True:cmdkeytool -keyalg RSA -genkeypair -alia…

【机器学习】 贝叶斯理论的变分推理

许志永 一、说明 贝叶斯原理&#xff0c;站在概率角度上似乎容易解释&#xff0c;但站在函数立场上就不那么容易了&#xff1b;然而&#xff0c;在高端数学模型中&#xff0c;必须要在函数和集合立场上有一套完整的概念&#xff0c;其迭代和运算才能有坚定的理论基础。 二、贝叶…

Qt能跨多少个平台?Qt能支持多少个平台?

2023年8月5日&#xff0c;周日下午 目录 Qt所支持的平台更多关于Qt支持的信息 Qt所支持的平台 图中显示的平台都支持。 想要更详细的平台支持信息可以查看&#xff1a;Supported Platforms | Qt 5.15 更多关于Qt支持的信息 Qt - 支持的平台及语言

【技巧】如何保护PowerPoint不被改动?

PPT&#xff0c;也就是PowerPoint&#xff0c;是很多小伙伴在工作生活中经常用到的图形演示文稿软件。 做好PPT后&#xff0c;担心自己不小心改动了或者不想他人随意更改&#xff0c;我们可以如何保护PPT呢&#xff1f;下面小编就来分享两个常用的方法&#xff1a; 1. 将PPT改…

Bert详细学习及代码实现详解

BERT概述 BERT的全称是Bidirectional Encoder Representation from Transformers&#xff0c;即双向Transformer的Encoder&#xff0c;因为decoder是不能获要预测的信息的。在大型语料库&#xff08;Wikipedia BookCorpus&#xff09;上训练一个大型模型&#xff08;12 层到 …

windows为nginx添加定时任务(开机延迟启动)

windows开机启动任务 调用定时任务管理器选中windows创建基本任务设置名称和描述设置触发器 并且添加个延迟触发设置操作设置条件配置设置 调用定时任务管理器 winr 输入 taskschd.msc回车 选中windows创建基本任务 设置名称和描述 设置触发器 并且添加个延迟触发 设置操作 …

深入学习 Redis - 事务、实现原理、指令使用及场景

目录 一、Redis 事务 vs MySQL事务 二、Redis 事务的执行原理 2.1、执行原理 2.2、Redis 事务设计这么简单&#xff0c;为什么不涉及成 MySQL 那样强大呢&#xff1f; 三、Redis 事务的使用 3.1、使用场景 3.2、具体演示 开启/执行/放弃事务 watch 监控 watch 实现原理…

Visual ChatGPT:Microsoft ChatGPT 和 VFM 相结合

推荐&#xff1a;使用 NSDT场景编辑器助你快速搭建可二次编辑的3D应用场景 什么是Visual ChatGPT&#xff1f; Visual ChatGPT 是一个包含 Visual Foundation 模型 &#xff08;VFM&#xff09; 的系统&#xff0c;可帮助 ChatGPT 更好地理解、生成和编辑视觉信息。VFM 能够指…

UML箭头汇总

参考&#xff1a;http://www.cnblogs.com/damsoft/archive/2016/10/24/5993602.html 1.UML简介 Unified Modeling Language (UML)又称统一建模语言或标准建模语言。 简单说就是以图形方式表现模型&#xff0c;根据不同模型进行分类&#xff0c;在UML 2.0中有13种图&#xff…

Hi,运维,你懂Java吗--No.9:线程池

作为运维&#xff0c;你不一定要会写Java代码&#xff0c;但是一定要懂Java在生产跑起来之后的各种机制。 本文为《Hi&#xff0c;运维&#xff0c;你懂Java吗》系列文章 第九篇&#xff0c;敬请关注后续系列文章 欢迎关注 龙叔运维&#xff08;公众号&#xff09; 持续分享运维…

8个最高效的Python爬虫框架,你用过几个?

前言 嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! 小编收集了一些较为高效的Python爬虫框架。分享给大家。 1.Scrapy Scrapy是一个为了爬取网站数据&#xff0c;提取结构性数据而编写的应用框架。 可以应用在包括数据挖掘&#xff0c;信息处理或存储历史数据等一系列的程…