【数据结构】链表基础知识讲解

news2024/9/30 15:22:38

文章目录

  • 链表
  • 链表的结构
  • 使用链表的优点
  • 模拟实现链表

链表

在之前的学习中我们讲解了顺序表ArrayList,Java模拟实现顺序表,如果需要大家可以去看一看,顺序表底层的实现逻辑其实就是数组,在物理存储结构和逻辑上都是连续的,而今天我们要学习的链表呢?链表是一种物理存储结构上不连续的存储结构,但是逻辑上链表又是连续的,这是如何实现的呢?下面通过一张图来给大家讲解。
在这里插入图片描述
链表是由很多个节点组成,每个节点中包含了两个东西一个是value用于储存数据,另一个是next用于储存下一个节点的地址,如果是双向链表还会多一个前驱prev储存的是前一个节点的地址。如上图,每个节点的存储地址并不连续,但是通过每个节点的next可以将节点都串起来,形成逻辑上的连续。向这样的存储结构我们成为链表

链表的结构

在链表中的结构非常多样,例如链表是双向还是单向,带不带头结点,循环或者是非循环链表。将这些要素组合起来一共可以组合成8种结构的链表。这些要素都是什么意思呢,我们还是通过画图来给大家讲解一下。

循环和非循环
在这里插入图片描述
例如:上一个链表最后一个节点为null,我们将null更改为第一个节点的地址,这样就是循环链表了。

是否带头节点
在这里插入图片描述
链表是否带头结点,就是看有没有一个固定的结点充当头节点

双向链表单向链表
在这里插入图片描述
像这样既可以从前向后访问节点,又可以从后向前回退回来的链表就是双向链表。单项链表顾名思义只能从前向后访问。

使用链表的优点

经过上面的讲解,大家都会发现这好像跟顺序表存储数据也差不多,那链表相较于顺序表有什么优点和注意事项呢?我们说顺序表在物理地址上必须是连续的,所以插入删除元素时需要整体挪动后续元素,效率较低,而链表在插入或者删除元素时只需要改变某一处节点的next指向的地址,就可以实现插入删除元素,更省时间

注意:
1、链式结构在逻辑上时连续的,但在物理上不一定连续。
2、结点一般都是从堆上申请出来的
3、从堆上申请的空间,是按照一定的策略来分配的,两次申请的空间可能连续,可能不连续。

模拟实现链表

说了这么多,让我们来模拟实现一下链表吧,我们写一个无头单向非循环的链表

  public class MyLinkedList {
  
   static class LinkNode {
        public int val;//用于储存数据
        public LinkNode next;//用于找到下一个节点
        public LinkNode(int val) {
            this.val = val;
        }
    }

    public LinkNode head;//假设充当一个头节点,不固定所以链表还是无头的。
 }

因为链表是由节点组成的,而节点内部由储存数据的空间和存储下个结点的地址空间组成,所以我们可以用内部类来解决这个问题。

头插法添加元素

   //头插法
    public void addFirst(int data){
        LinkNode newNode = new LinkNode(data);
        newNode.next = head;
        head = newNode;
    }

头插法首先我们需要创建一个新的节点用于存储先添加的元素数据,因为是头插法,是在链表的最前面插入元素,所以新创建的节点的next需要指向head结点(我们假设的头结点),并且将新的节点设置为head
在这里插入图片描述
我们可以写一个打印链表的方法验证一下:

打印链表:

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

因为最后一个节点的next等于null,所以如果希望遍历完链表就让cur != null,如果遍历到最后一个节点就停下来就cur.next != nullcur == cur.next就相当于顺序表中数组下标的i++,每次打印节点用于储存数据的value就可以完成遍历打印链表。
在这里插入图片描述

得到单链表的长度:

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

同样我们需要遍历链表,将计数器count++就可以统计链表节点的个数,最后就是链表的长度。
在这里插入图片描述

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

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

同样需要遍历链表,将节点的value的值跟需要查找的key值做比较,如果相同说明存在就返回true如果cur == null说明遍历完链表没有与key相同的元素,就返回false
在这里插入图片描述

尾插法:

既然我们有头插法,那肯定会有尾插法,在链表最后插入元素:

 //尾插法
    public void addLast(int data){
        LinkNode newNode = new LinkNode(data);

        if(head == null) {
            newNode = head;
        }

        LinkNode cur = head;
        while (cur.next != null) {//找到最后一个结点停下来
            cur = cur.next;
        }
        cur.next = newNode;
    }

跟头插法一样,我们还是需要新建一个节点用于储存新的元素,如果链表为空,那么新插入的节点就是我们假设的头节点,如果不为空,那就遍历链表找到最后一个节点,让最后一个节点的next指向新的结点。
在这里插入图片描述
在这里插入图片描述

任意位置插入,第一个数据节点为0号下标:

 //任意位置插入,第一个数据节点为0号下标
    public void addIndex(int index,int data){
        try {
            checkIndex(index);
        } catch (ListIndexException ex) {
            ex.printStackTrace();
        }

        if(index == 0) {
            addFirst(data);
            return ;
        }

        if(index == size()) {
            addLast(data);
            return ;
        }

        LinkNode tmp = FindIndexNode(index);
        LinkNode newNode = new LinkNode(data);
        newNode.next = tmp.next;
        tmp.next = newNode;
    }

    private LinkNode FindIndexNode(int index) {//找到index位置的前一个结点
        LinkNode cur = head;
        int count = 0;
        while (count != index - 1) {
            cur = cur.next;
            count++;
        }
        return cur;
    }
    private void checkIndex(int index) throws ListIndexException{
        if(index < 0 || index > size()) {
            throw new ListIndexException("index下标错误");
        }
    }

首先我们需要插入的位置进行判断是否合法,其次如果index为0那么就是头插,如果index为链表的长度,那就是尾插,如果是其他位置就需要,找到该位置的前一个位置停下来,让新节点的next指向index位置的节点,让index - 1位置的节点的next指向新节点,就完成了插入。
在这里插入图片描述
注意节点next的赋值顺序不能交换,如果先把index - 1位置结点的next替换为newNode就找不到index位置节点的地址了。
在这里插入图片描述

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

 //删除第一次出现关键字为key的节点
    public void remove(int key){
        if(head == null) {
            return ;
        }
        if(head.val == key) {
            head = head.next;
        }

        LinkNode prev = SearchRemoveNode(key);
        if(prev == null) {
            System.out.println("没有要删除的结点");
            return ;//没有要删除的结点
        }
        LinkNode del = prev.next;
        prev.next = del.next;
    }
    private LinkNode SearchRemoveNode(int key) {
        LinkNode cur = head;
        while (cur.next != null) {
            if(cur.next.val == key) {//找到要删除结点的前一个结点
                return cur;
            }
            cur = cur.next;
        }
        return null;
    }

首先我们需要进行判断,如果链表为空那么就返回,说明没有要删除的元素,如果头节点为该元素那么就让头节点向后移动一下,判断都不符合之后,我们需要找到要删除元素的前一个元素,将前一个元素的next改为要删除元素的next就完成了删除。
在这里插入图片描述

在这里插入图片描述

删除所有值为key节点:

  //删除所有值为key的节点
    public void removeAllKey(int key){
        if(head == null) {
            return ;
        }

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

    }

使用的逻辑跟删除一个元素是一样的,我们只需要遍历链表,找到需要删除的元素时执行一次删除一个元素的逻辑,就可以了,值得注意的是每次删除元素后不要第一时间将prev == cur因为可能删除后的cur还是一个需要删除的节点,在进行判断当前cur节点不是删除元素后在进行操作,这样遍历完后除了head结点剩下的节点中值为key的都被删除了,最后判断一下head节点是否需要删除,就完成了删除所有值为key的节点。

在这里插入图片描述

清空链表:

public void clear() {
        head = null;
    }

因为无论干什么都需要通过head节点来访问,将head置为null后面的节点也就都找不到了,达成了清空链表的需求。
在这里插入图片描述
以上就是链表的基础知识。

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

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

相关文章

Eth 03 -以太网驱动Eth的配置

以太网的配置,下面这张图描述了以太网的配置参数: EthCtrlConfig:单个控制器的配置EthCtrlEnableMii :启用/禁用用于收发器访问的媒体独立接口 (MII)EthCtrlEnableRxInterrupt:启用/禁用接收中断EthCtrlEnableTxInterrupt:启用/禁用传输中断EthCtrlIdx:指定已配置控制…

【BUUCTF】MISC(第一页wp)

文章目录签到金三胖二维码你竟然赶我走大白N种方法解决乌镇峰会种图基础破解wireshark文件中的秘密图片exifLSBLSB隐写&#xff08;最低有效位隐写&#xff09;&#xff1a;zip伪加密ZIP 文件由**三个部分**组成&#xff1a;**压缩源文件数据区**&#xff1a;**压缩源文件目录区…

FS4412环境搭建

目录 一、开发板硬件资源介绍 二、交叉开发环境 2.1安装交叉编译工具链 2.2配置全局变量​编辑 2.3测试​编辑 2.4终端 2.5安装串口驱动 2.6上电测试 三、地址映射表 一、开发板硬件资源介绍 中间红色的是samsung的主控&#xff0c;四个粉色的256M的内存条&#xff0…

STM32F4SysTick记录

滴哒主要用于延时和实时系统 模板为原子串口实验源码&#xff0c;入口为24行 120行为滴哒定时器的CTRL寄存器位时钟源设置 这个参数的必要性是用于溢出时间的计算参数之一 可以设置为HCLK或HCLK的8分频 延时函数理解 设置LOAD是设置重装载值 设置VAL清空计数值以及标志位 …

强大的ANTLR4(2)

每次在命令行里输入文本有点麻烦&#xff0c;可以将hello slb保存于hello.txt文本文件中&#xff0c;然后运行命令&#xff1a; antlr4-parse Hello.g4 r -tokens hello.txt出现如下内容&#xff1a; [0,0:4hello,<hello>,1:0] [1,6:8slb,<ID>,1:6] [2,9:8<EO…

JDBC开荒

docker 创建MySQL 一、简介 Java DataBase Connectivity &#xff0c;是Java程序访问数据库的标准接口 Java访问DB的时候&#xff0c;并不是直接通过TCP连接的&#xff0c;而是通过JDBC接口&#xff0c;而JDBC接口又是通过JDBC驱动来访问的 JDBC是Java标准库自带的&#xff0…

(HP)next.js入门

推荐文档&#xff1a;生成<head> - 《next.js v7.0 中文文档》 - 书栈网 BookStack 1&#xff0c;解决的问题 SPA单页面应用的两个问题&#xff1a;首屏加载过慢&#xff0c;不能SEO(搜索引擎抓取&#xff09; 2&#xff0c;它是一个react服务端渲染框架 3&#xff0c;…

ArcGIS去除黑边方法汇总

概述 在使用ArcGIS对影像进行应用的时候&#xff0c;如果出现了黑边&#xff0c;除了影响美观之外&#xff0c;进行镶嵌处理也可能会有问题&#xff0c;这里&#xff0c;我们介绍一下几种ArcGIS去除黑边的方法&#xff0c;希望能够对大家有所帮助。 数据来源 教程所使用的实…

【C++进阶】类型转换

&#x1f387;C学习历程&#xff1a;入门 博客主页&#xff1a;一起去看日落吗持续分享博主的C学习历程博主的能力有限&#xff0c;出现错误希望大家不吝赐教分享给大家一句我很喜欢的话&#xff1a; 也许你现在做的事情&#xff0c;暂时看不到成果&#xff0c;但不要忘记&…

蓝桥杯Python练习题8-查找整数

资源限制   内存限制&#xff1a;256.0MB C/C时间限制&#xff1a;1.0s Java时间限制&#xff1a;3.0s Python时间限制&#xff1a;5.0s 问题描述   给出一个包含n个整数的数列&#xff0c;问整数a在数列中的第一次出现是第几个。 输入格式   第一行包含一个整数n。  …

欢迎谷歌回归中国,但有前提!李彦宏也发了条朋友圈

2018年&#xff0c;Google部分功能成功回归中国大陆 周一&#xff0c;人民日报在海外社交媒体平台Twitter和Facebook上刊文&#xff0c;针对谷歌计划以过滤版搜索引擎重返中国大陆的消息回应称&#xff0c;欢迎谷歌重返中国大陆&#xff0c;但前提是必须遵守中国法律。李彦宏在…

云计算IaaS、PaaS(iPaaS/aPaaS)以及SaaS以及发展趋势

一、云计算IaaS、PaaS以及SaaS架构 云计算涉及了很多产品与技术&#xff0c;表面上看起来的确有点纷繁复杂&#xff0c;但是云计算本身还是有迹可循和有理可依的&#xff0c;下面介绍一套云计算的架构&#xff0c;具体请看图&#xff1a; 上面这个云架构共分为服务和管理这两…

车载入侵检测与防御系统介绍

作者 | 田铮 上海控安可信软件创新研究院项目经理 来源 | 鉴源实验室 引言&#xff1a;上一篇文章&#xff08;智能网联汽车网络安全攻击与防御技术概述&#xff09;介绍了智能网联汽车中的网络安全攻击案例和具体攻击类型。而本篇文章中&#xff0c;我们将对汽车网络安全风险…

SpringSecurity——OAuth2框架鉴权实现源码分析

SpringSecurity——OAuth2框架鉴权实现源码分析一、ManagedFilter迭代过滤器链1.4 springSecurityFilterChain1.4.7 OAuth2AuthenticationProcessingFilter①.OAuth2AuthenticationProcessingFilter.class②.CookieTokenExtractor.class&#xff08;我们自己重写的方法&#xf…

瞄准智慧园区 东方恩拓与用友协同开拓新商机

在数字化转型升级浪潮中&#xff0c;传统园区也在寻求新的发展方向&#xff0c;从传统园区向智慧园区甚至未来园区不断演进。随着国家“数字中国”、“中国智造”、“新基建”等战略的部署&#xff0c;智慧园区也迎来了新的发展机遇&#xff0c;园区的数字化、网络化、智能化是…

户外运动耳机选择哪个、最适合户外运动的蓝牙耳机推荐

还有哪个季节比秋天更适合爬山和徒步等户外运动的吗&#xff1f;秋天—没有夏日的骄阳&#xff0c;没有冬天的万物凋零&#xff0c;放眼望去都是墨绿和金黄&#xff0c;上山的话还可以采摘成熟的各种各样的果子…但是一个人的话难免有些落寞&#xff0c;要是有音乐的陪伴则会增…

公网远程连接windows SQL Server数据库【内网穿透】

文章目录1. 本地安装配置SQL Server2. 将本地sqlserver服务暴露至公网2.1 本地安装cpolar内网穿透2.2 创建隧道3. 公网远程连接sqlserver3.1 使用命令行远程连接sqlserver3.2 使用navicat premium图形界面远程连接sqlserver3.3 使用SSMS图形界面远程连接sqlserver疫情当下,居家…

win7电脑怎么录屏?超级简单的2种电脑录屏方法

相信还有不少朋友的电脑系统是win7系统。其实&#xff0c;win7电脑和win10电脑一样都有自带录屏功能。那win7电脑怎么录屏&#xff1f;在win7电脑上开启运行窗口即可&#xff0c;当然也可以通过使用专业的录屏软件来录制屏幕。 下面就由小编给大家介绍一下2个win7电脑录屏的方…

P8 PyTorch WhereGather

前言 这两个函数优点是通过GPU 运算速度快 目录&#xff1a; 1 where 2 Gather 一 where 原理&#xff1a; torch.where(condition,x,y) 输入参数&#xff1a; condition: 判断条件 x,y: Tensor 返回值&#xff1a; 符合条件时: 取x, 不满足取y 优点&#xff1a; 可以使…

关注re:Invent中国巡展,尽享数字时代红利

编辑&#xff5c;阿冒收获固然很甜蜜&#xff0c;但是收获也很辛苦。肯定会有人感觉莫名其妙&#xff0c;既然是收获&#xff0c;必然是甜蜜的&#xff0c;哪来的辛苦啊&#xff1f;且不要着急&#xff0c;容我慢慢分说。一年一度的亚马逊云科技re:Invent全球大会&#xff0c;向…