【数据结构】 链表简介与单链表的实现

文章目录

  • ArrayList的缺陷
  • 链表
    • 链表的概念及结构
    • 链表的分类
      • 单向或者双向
      • 带头或者不带头
      • 循环或者非循环
  • 单链表的实现
    • 创建单链表
    • 遍历链表
    • 得到单链表的长度
    • 查找是否包含关键字
    • 头插法
    • 尾插法
    • 任意位置插入
    • 删除第一次出现关键字为key的节点
    • 删除所有值为key的节点
    • 回收链表
  • 总结

ArrayList的缺陷

在【数据结构】 ArrayList简介与实战中我们已经熟悉了ArrayList的使用,并且进行了简单模拟实现。通过源码知道,ArrayList底层使用数组来存储元素

由于其底层是一段连续空间,当在ArrayList任意位置插入或者删除元素时,就需要将后序元素整体往前或者往后搬移,时间复杂度为O(n),效率比较低,因此ArrayList不适合做任意位置插入和删除比较多的场景。 因此:java集合中又引入了LinkedList,即链表结构

链表

链表的概念及结构

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

就如同火车中间通过链条链接的一个道理
在这里插入图片描述

链表的大概结构如下所示:

在这里插入图片描述

链表的每一节都含有相应的数据与下一节的地址,一般我们会记录起始节点,可以用来我们访问该链表,最后一个节点不含有下一节点的地址,置为null;

链表的分类

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

单向或者双向

在这里插入图片描述

带头或者不带头

在这里插入图片描述

循环或者非循环

在这里插入图片描述
虽然有这么多的链表的结构,但是我们重点掌握两种:

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

单链表的实现

接下来我们来实现一个无头单向非循环链表实现,并实现一些相应的功能

创建单链表

博主在这里创建一个类为MyLinkedList类,我们将在这里面实现我们的无头单向非循环链表

创建单链表其实很简单,我们需要建立一个类,类里面放你需要放的数据元素,以及该类类型的元素,用于储存下一节点的地址

然后我们还需要一个变量来记录其实节点,在这里我们用一个内部类在MyLinkedList类进行表示,并在createLink方法里进行各个节点的创建并赋值,然后将这些节点链接起来

在这里插入图片描述

代码实现如下

public class MyLinkedList {class Node {public int val;public Node next;public Node(int val) {this.val = val;}}public Node head;// 代表当前链表的头节点的引用public void createLink() {Node node1 = new Node(11);Node node2 = new Node(22);Node node3 = new Node(33);Node node4 = new Node(44);node1.next = node2;node2.next = node3;node3.next = node4; /* */head = node1;}
}

遍历链表

写好的链表,我们来进行打印一下看看是否创建成功

我们的head为我们的头节点,也就是我们遍历的起点,但是由于该单链表是单向的,如果使用头节点进行遍历的话,会导致后面找不到头节点,所以我们这里重新创建一个变量进行遍历。

由于每一节点里的next里面都存储着我们下一节点的地址,所以当我们访问完当前节点的元素后,将当前节点的next赋给我们所创建的遍历变量即可;

由于最后一个节点里next里面为null,所以我们将它作为结束条件

代码实现如下

public void disPlay() {Node sur = head;while(sur  != null) {System.out.print(sur.val+" ");sur = sur.next;}}

得到单链表的长度

进行遍历,并用一个数进行记录,最后进行返回就行

代码实现如下

 public int size(){int count = 0;Node cur = head;while (cur != null) {count++;cur = cur.next;}return count;}

查找是否包含关键字

我们只需要对该链表进行遍历,然后一一比对就好,大致实现过程与遍历链表相同

代码实现如下

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

头插法

将第一个节点赋给我们新添加的节点里面的next

并且将新添加的节点赋给head,作为新的头节点
在这里插入图片描述

代码实现如下

  public void addFirst(int data){Node node = new Node(data);node.next = head;head = node;}

注意:一定要注意赋值的顺序

尾插法

我们首先对该链表进行遍历,当遍历到最后一个节点时

将最后一个节点里的next赋为我们新增的节点即可。

如果该链表为空,直接将该新增节点设为头节点就好
在这里插入图片描述

代码实现如下

 public void addLast(int data){Node node = new Node(data);if(head == null) {head = node;return;}Node cur = head;while (cur.next != null) {cur = cur.next;}cur.next = node;}

任意位置插入

首先我们需要对需要插入的位置进行遍历,必须为合法,如果不合法,我们会抛出一个异常进行提醒,这里我们自定义了一个异常如下

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

任意位置插入,我们可以分为种情况

  • 插在开头
  • 插在结尾
  • 插在中间

前两种我们已经实现了,我们现在只需要实现插在中间就好

我们对该单链表进行编号,第一个节点为我们的0下标。我们需要遍历单链表,找到所需要插入下标的前一个节点

将该节点的next设为我们新增的节点,将新增节点里面的next设为下一节点
在这里插入图片描述
代码实现如下

    //任意位置插入,第一个数据节点为0号下标public void addIndex(int index,int data)throws ListIndexOutOfException{checkIndex(index);if(index == 0) {addFirst(data);return;}if(index == size()) {addLast(data);return;}Node cur = findIndexSubOne(index);Node node = new Node(data);node.next = cur.next;cur.next = node;}/*** 找到 index-1位置的节点的地址* @param index* @return*/private Node findIndexSubOne(int index) {Node cur = head;int count = 0;while (count != index-1) {cur = cur.next;count++;}return cur;}private void checkIndex(int index) throws ListIndexOutOfException{if(index < 0 || index > size()) {throw new ListIndexOutOfException("index位置不合法");}}

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

分为四种情况

  • 一个节点都没有
  • 删除数据在第一个
  • 没有你要删除的数据
  • 有你要删除的数据且不是第一个

第一种情况判断是否为空后直接返回就好

第二种情况直接将头节点置为下一节点,也就时head=head.next;

第三种情况遍历单链表后,返回就好

第四种情况,找到所需要删除数据的前一节点,将所需要删除数据的前一节点的next设为当前需要删除数据节点的next
在这里插入图片描述

代码实现如下

//删除第一次出现关键字为key的节点 O(N)public void remove(int key){if(head == null) {return ;//一个节点都没有}if(head.val == key) {head = head.next;return;}Node cur = searchPrev(key);if(cur == null) {return;}Node del = cur.next;//要删除的节点cur.next = del.next;}/*** 找到关键字key的前一个节点* @param key* @return*/private Node searchPrev(int key) {Node cur = head;while (cur.next != null) {if(cur.next.val == key) {return cur;}cur = cur.next;}return null;//没有你要删除的节点}

删除所有值为key的节点

我们依旧需要对该单链表进行判断,如果为空,就直接返回

由于我们需要删除很多个这样的节点,但是我们的单链表却是单向的,按照上面的写法,我们则需要遍历很多次单链表,大大的增加了复杂度,我们为了降低时间复杂度,使它降为O(N);

我们设两个遍历节点,进行遍历,一个在前为cur,一个在后prev
在这里插入图片描述

前面的cur负责进行遍历删除,后面的prev负责跟在cur后面记录cur的上一节点

当cur下一节点不是我们所要删除的元素时,这时候我们将prev变为我们当前节点的cur,而cur变为当前节点的next
在这里插入图片描述
当前的删除方法只能删除第一个节点以后的元素,所以我们还需要处理第一个元素是我们所需要删除的情况。

代码实现如下

//删除所有值为key的节点public void removeAllKey(int key){if(head == null) {return;}Node prev = head;Node 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;}}

回收链表

将头节点置为空就好。

代码实现如下

public void clear() {head = null;}

总结

关于《【数据结构】 链表简介与单链表的实现》就讲解到这儿,感谢大家的支持,欢迎各位留言交流以及批评指正,如果文章对您有帮助或者觉得作者写的还不错可以点一下关注,点赞,收藏支持一下!

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

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

相关文章

案例-基于MVC和三层架构实现商品表的增删改查

文章目录 0. 项目介绍1. 环境准备2. 查看所有2.1 编写BrandMapper接口2.2 编写服务类&#xff0c;创建BrandService&#xff0c;用于调用该方法2.5 编写Servlet2.4 编写brand.jsp页面2.5 测试 3.添加3.1 编写BrandMapper接口 添加方法3.2 编写服务3.3 改写Brand.jsp页面&#x…

CMake教程6:调用lib、dll

之前我们学到了如何编写一个可执行程序和Library&#xff0c;在继续学习之前&#xff0c;需要解释下target&#xff0c;在cmake中我们可以给executable和library设置一个target名字&#xff0c;这样可以方便我们在后续对target进行更加详细的属性设置。 本节我们将学习如何在项…

利用logstash/filebeat/插件,将graylog日志传输到kafka中

1.graylog配置输出 在System-outputs&#xff0c;选择GELF Output&#xff0c;填写如下内容&#xff0c;其它选项默认 在要输出的Stream中&#xff0c;选择Manage Outputs 选择GELF Output&#xff0c;右边选择刚才创建好的test。 2.安装logstash&#xff0c;作为中间临时…

Vue使用jspdf和html2canvas组件库结合导出PDF文件

效果图&#xff1a; 1、安装依赖&#xff1a; npm install html2canvas --save npm install jspdf --save 或 yarn add html2canvas --save yarn add jspdf --save 2、封装全局调用方法&#xff1a;this.$exportPDF(#id,文件名) 新建js文件&#xff1a;/utils/html2Pdf.js&am…

数据结构:选择排序

简单选择排序 选择排序是一种简单直观的排序算法。首先在未排序序列中找到最大&#xff08;最小&#xff09;的元素&#xff0c;存放到排序学列的其实位置&#xff0c;然后在剩余的未排序的元素中寻找最小&#xff08;最大&#xff09;元素&#xff0c;存放在已排序序列的后面…

【深度学习】PyTorch快速入门

【深度学习】学习PyTorch基础 介绍PyTorch 深度学习框架是一种软件工具&#xff0c;旨在简化和加速构建、训练和部署深度学习模型的过程。深度学习框架提供了一系列的函数、类和工具&#xff0c;用于定义、优化和执行各种深度神经网络模型。这些框架帮助研究人员和开发人员专注…

嵌入式Linux开发实操(九):CAN接口开发

前言: CAN网络在汽车中的使用可以说相当广泛。而CAN网络需要的收发器最常用的就是NXP 的TJA1042: CAN网络:

无涯教程-Perl - splice函数

描述 此函数从LENGTH元素的OFFSET元素中删除ARRAY元素,如果指定,则用LIST替换删除的元素。如果省略LENGTH,则从OFFSET开始删除所有内容。 语法 以下是此函数的简单语法- splice ARRAY, OFFSET, LENGTH, LISTsplice ARRAY, OFFSET, LENGTHsplice ARRAY, OFFSET返回值 该函数…

Vue 项目运行 npm install 时,卡在 sill idealTree buildDeps 没有反应

解决方法&#xff1a;切换到淘宝镜像。 以下是之前安装的 xmzs 包&#xff0c;用于控制切换淘宝镜像。 该截图是之前其他项目切换淘宝镜像的截图。 切换镜像后&#xff0c;顺利执行 npm install 。

生成国密密钥对

在线生成国密密钥对 生成的密钥对要妥善保管&#xff0c;丢失是无法找回的。

selinux

一、selinux的说明 二、selinux的工作原理 三、selinux的启动、关闭与查看 Enforcing和permissive都是临时的&#xff0c;重启还是依据配置文件中&#xff0c;禁用selinux&#xff0c;修改配置文件&#xff1a; 之后重启生效 四、selinux对linux服务的影响

【JAVA】我们该如何规避代码中可能出现的错误?(一)

个人主页&#xff1a;【&#x1f60a;个人主页】 系列专栏&#xff1a;【❤️初识JAVA】 文章目录 前言三种类型的异常异常处理JAVA内置异常类Exception 类的层次 前言 异常是程序中的一些错误&#xff0c;但并不是所有的错误都是异常&#xff0c;并且错误有时候是可以避免的&…

Grounding dino + segment anything + stable diffusion 实现图片编辑

目录 总体介绍总体流程 模块介绍目标检测&#xff1a; grounding dino目标分割&#xff1a;Segment Anything Model (SAM)整体思路模型结构&#xff1a;数据引擎 图片绘制 集成样例 其他问题附录 总体介绍 总体流程 本方案用到了三个步骤&#xff0c;按顺序依次为&#xff1a…

Tomcat 部署优化

Tomcat Tomcat 开放源代码web应用服务器&#xff0c;是由java代码开发的 tomcat就是处理动态请求和基于java代码的页面开发 可以在html当中写入java代码&#xff0c;tomcat可以解析html页面当中的iava&#xff0c;执行动态请求 动态页面机制有问题&#xff1a;不对tomcat进行优…

vue 使用indexDB 简单完整逻辑

1 npm npm install idb 2 代码 <template><div><p>Data: {{ data }}</p><button click"fetchData">Fetch Data</button></div> </template><script> import { openDB } from idb;export default {data() {…

eqtl-GWAS和GWAS-GWAS

目前教程中有eqtl-GWAS和GWAS-GWAS两种模式&#xff0c;其他模式比较少见&#xff0c;还未进行开发 数据类型cc为分类变量即case/control&#xff0c;quant为连续变量&#xff0c;eqtl数据默认quant coloc.abf有两个比较需要注意的点&#xff0c;就是数据集中N是代表样本量&am…

[Docker精进篇] Docker镜像构建和实践 (三)

前言&#xff1a; Docker镜像构建的作用是将应用程序及其依赖打包到一个可移植、自包含的镜像中&#xff0c;以便在不同环境中快速、可靠地部署和运行应用程序。 文章目录 Docker镜像构建1️⃣是什么&#xff1f;2️⃣为什么&#xff1f;3️⃣镜像构建一、用现有容器构建新镜像…

【IMX6ULL驱动开发学习】08.马达驱动实战:驱动编写、手动注册平台设备和设备树添加节点信息

目录 一、使用设备树 1.1 修改设备树流程 二、手动创建平台设备 三、总结&#xff08;附驱动程序&#xff09; 前情提要&#xff1a;​​​​​​​【IMX6ULL驱动开发学习】07.驱动程序分离的思想之平台总线设备驱动模型和设备树_阿龙还在写代码的博客-CSDN博客 手动注册…

Android中如何不编译源生模块

如果想让自己的app 替换系统的app 比如使用闪电浏览器替换系统的Browser 首先把闪电浏览器放到 vendor/rockchip/common/apps Android.mk LOCAL_PATH : $(call my-dir) include $(CLEAR_VARS)LOCAL_MODULE : Lightning LOCAL_SRC_FILES : $(LOCAL_MODULE).apk LOCAL_MODULE_C…

linux 命令- systemctl

systemctl 参数说明 1、使用语法 用法&#xff1a;systemctl [OPTIONS…] {COMMAND} … 2 、参数说明 参数参数说明start立刻启动后面接的unitstop立刻关闭后面接的unitrestart立刻关闭后启动后面接的unit&#xff0c;亦即执行stop再start的意思reload不关闭后面接的unit的…