【数据结构】3000字剖析链表及双向链表

文章目录

  • 💐 链表的概念与结构
    • 💐链表的介绍
      • 💐链表的模拟实现
    • 💐双向链表
      • 💐双向链表的模拟实现
    • 💐链表常用的方法
    • 💐链表及顺序表的遍历
    • 💐ArrayList和LinkedList的差异

💐 链表的概念与结构

前面讲解了 ArrayList 实现的顺序表,下面讲解线性表的另一种存储结构 链表

在顺序表中,所有的元素都是在一段地址连续的存储单元上保存的,每个元素挨着每一个元素,通过下标也可以进行访问;但缺点是在非首尾的插入和删除时,需要移动大量的元素,时间复杂度较高;
在这里插入图片描述

所以,为了解决这个问题,就衍生出了另一种数据结构——链表

链表链表是用一段物理地址不连续的存储单元依次存储数据的线性结构;

有n个节点组成,每个节点里面除了存储数值以外,还保存了下一个节点的地址,这样就可以通过地址依次找到对应的下一个节点;
在这里插入图片描述
在这里插入图片描述

💐链表的介绍

在这里插入图片描述

2、有头或者无头链表
在这里插入图片描述
3、循环或非循环链表
在这里插入图片描述

根据以上三种链表结构又可以组合成一下八种结构:
在这里插入图片描述
而最常用的是以上无头单向非循环链表无头双向链表,下面就针对于两种链表进行详细的剖析!!!

💐链表的模拟实现

要想深刻的理解链表,就要亲手实现一下,这样才能深切的领会到链表他是怎样进行操作的,如何使用的!!!

首先,先思考,如果要实现一个链表的话,都要有哪些功能呢?下面我实现了一个链表功能,我将针对实现的链表每一部分进行一个剖析:

要实现一个链表,首先实现以下几个基础的功能:

1、节点的增加

2、节点的删除

3、数据的查找

4、节点的插入

//无头单向非循环链表实现
public class LinkedList {static class listNode {private int value;//结点中的值private listNode next;//下一个结点的地址public listNode(int value) {this.value = value;}}//创建一个链表public void createList() {ListNode listNode1 = new ListNode(20);ListNode listNode2 = new ListNode(30);ListNode listNode3 = new ListNode(40);ListNode listNode4 = new ListNode(50);ListNode listNode5 = new ListNode(60);listNode1.next = listNode2;listNode2.next = listNode3;listNode3.next = listNode4;listNode4.next = listNode5;this.head = listNode1.next;}private listNode head;//第一个结点的地址//头插法public void addHead(int val) {ListNode listNode = new ListNode(val);listNode.next = head;head = listNode;return;      }//尾插法public void addLast() {ListNode listNode = new ListNode(val);ListNode cur = head;while(cur.next != null) {cur = cur.next;}cur.next = listNode;}//任意位置插入public void addIndex() {ListNode listNode = new ListNode(value);if(index == 0) {addHead(value);return;}if(index >figureSize() || index<0) {throw new IndexException("输入下标下标有误");}ListNode cur = head;ListNode pos = checkIndex(cur, index);//定义一个查找前一个节点的方法listNode.next = pos.next;pos.next = listNode;}//查找前一个节点方法public  static  ListNode checkIndex(ListNode cur, int index) {for(int i =0; i<index-1; i++) {cur = cur.next;}return cur;}//查找链表中是否包含查找的值public void contains() {ListNode cur = head;for(int i =0; i<figureSize(); i++) {if(cur.value == key) {System.out.println("链表中包含查找的值");return;}cur = cur.next;}System.out.println("链表中不包含查找的值");}//删除第一次出现的指定的值的结点public void delFirst(int key) {if(head == null) {return;}if(key == head.value) {head = head.next;return;}ListNode cur =  searchVal(key);if(cur == null) {System.out.println("没有您要删除的值");}assert cur != null;ListNode del = cur.next;cur.next = del.next;}//负责查找前一个节点public ListNode searchVal(int key) {ListNode cur = head;for(int i =0; i<figureSize(); i++) {if(key == cur.next.value) {return cur;}cur = cur.next;}return null;}//删除所有值为key的结点public void allPoint(int key) {if(head == null) {return;}ListNode prev = head;ListNode cur = head.next;while(cur != null){if(cur.value == key) {prev.next = cur.next;cur = cur.next;}else {prev = cur;cur = cur.next;}if(head.value == key) {head = head.next;}}}//打印链表public void disPlay() {ListNode cur = head;while(cur != null) {System.out.println(cur.value +" ");cur = cur.next;}}//计算单链表的长度public void figureSize() {int count = 0;ListNode cur = head;while(cur!=null) {count++;cur = cur.next;}return count;}//清空节点public void clear() {head = null;}}

下面对这些功能进行一一的实现:

1、如何创建一个结点

在实现这些功能前,需要先知道如何去写这个结点的代码,下图讲解:
在这里插入图片描述
在这里插入图片描述
2、创建一个链表

在已经创建好一个结点的基础上,如何创建链表呢,下面来实现一下:
在这里插入图片描述

1、头插法
在这里插入图片描述

2、尾插法
在这里插入图片描述
3、任意位置插入
在这里插入图片描述
在这里插入图片描述

4、查找链表中是否包含查找的值
在这里插入图片描述

5、删除第一次出现的指定的值的结点

在这里插入图片描述
在这里插入图片描述

6、删除所有值为key的节点

在这里插入图片描述
在这里插入图片描述

6、清除节点

直接将头节点置为空,因为只要头节点中的地址为空了,那么后面的结点就没有被引用指向了,所以就会被内存回收
在这里插入图片描述

💐双向链表

单向链表在使用上有以下几个缺点:

1、在删除和尾插时,时间复杂度较高

2、只能从前向后访问,无法从后向前访问,限制较大

而下面,介绍的双向链表是非常牛掰的,包括以后在实现栈、队列、双向队列等方面,使用双向链表会方便很多,下面先简单介绍一下双向链表:
在这里插入图片描述

💐双向链表的模拟实现

如果想要彻底的理解链表的话,我的建议是,自己多模拟实现几遍这个链表,这样可以更加有利于你灵活的去使用,而不是只记住它的方法的功能。

//无头双向链表的实现
public class LinkedList {static class ListNode{private int val;private ListNode next;private ListNode prev;public ListNode(int val) {this.val = val;}}private ListNode head;private ListNode last;//头插法public void addFirst(int val) {ListNode node = new ListNode(val);//情况一:链表中无节点if(head == null) {head = node;last = node;return;}//情况二:链表中无节点node.next = head;head.prev = node;head = node;}//尾插法public void addLast(int val) {ListNode node = new ListNode(val);//情况一:链表中无节点if(head == null && last == null) {head = node;last = node;}//情况二:链表中有节点last.next = node;node.prev = last;last = last.next;}//任意位置插入public void addIndex(int pos,int val) {ListNode node = new ListNode(val);if(pos == 0) {addFirst(val);return;}if(pos == size()) {addLast(val);return;}//寻找要插入节点的位置ListNode cur = searchIndex(pos);cur.prev.next = node;node.next = cur;node.prev = cur.prev;cur.prev = node;}public ListNode searchIndex(int pos) {ListNode cur = head;for(int i =0; i<pos; i++) {cur = cur.next;}return cur;}//查找关键字key是否包含在链表中public void contains(int key) {if(searchKey(key)) {System.out.println("找到了!!!");}else{System.out.println("链表中不包含此数据!!!");}}public boolean searchKey(int key) {ListNode cur = head;while(cur != null && cur.val != key) {cur = cur.next;}if(cur != null) return true;return false;}//删除第一次出现关键字为key的节点public void remove(int key) {ListNode cur = searchFirst(key);if(cur == null) {System.out.println("链表中不包含此数据!!!");}//情况一:头节点为key时if(cur == head) {//只有一个节点时if(head == last) {head = null;last = null;}else {//多个节点时head = head.next;head.prev = null;}}else if(cur == last) {//情况二:尾结点为key时last = last.prev;last.next = null;}else {cur.prev.next = cur.next;cur.next.prev = cur.prev;}}public ListNode searchFirst(int key) {ListNode cur = head;while(cur != null && cur.val != key) {cur = cur.next;}if(cur != null) {return cur;}return null;}//删除所有值为key的节点public void removeAll(int key) {ListNode cur = head;while(cur != null) {cur = searchFirst(key);if(cur == null) {return;}//为头节点时//情况一:头节点为key时if(cur == head) {//只有一个节点时if(head == last) {head = null;last = null;}else {//多个节点时head = head.next;head.prev = null;}}else if(cur == last) {//情况二:尾结点为key时cur.prev.next = null;last = null;}else {cur.prev.next = cur.next;cur.next.prev = cur.prev;}}}//链表的长度public int size() {ListNode cur = head;int size = 0;while(cur != null) {size++;cur = cur.next;}return size;}//打印单链表public void display() {ListNode cur = head;while(cur != null) {System.out.print(cur.val+" ");cur = cur.next;}}//清空链表public void clear() {head = null;last = null;}
}

这里双链表与单链表的模拟非常相似,只要多多注意几种情况即可;

💐链表常用的方法

1、LinkedList的构造方法

方法解释
LinkedList()无参构造
public LinkedList(Collection< ? extends E > c)使用其他集合容器中元素构造List
    public static void main(String[] args) {//使用无参构造方法List<Integer> list = new LinkedList<>();list.add(1);list.add(2);list.add(3);System.out.println(list);//使用list构造LinkedListList<Integer> list2 = new LinkedList<>(list);list2.add(4);System.out.println(list2);}

在这里插入图片描述

2、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 位置的下标

以上方法与ArrayList中的使用都是一样的,这里就不再一一列举!

💐链表及顺序表的遍历

1、for循环的方式遍历

2、foreach的方式遍历

3、Iterator迭代器遍历

 public static void main(String[] args) {LinkedList<Integer> list = new LinkedList<>();list.add(1);list.add(2);list.add(3);list.add(4);//foreach方式遍历for(int x: list) {System.out.print(x+" ");}System.out.println();//Iterator迭代器遍历--正向遍历Iterator<Integer> it = list.iterator();while(it.hasNext()) {System.out.print(it.next()+" ");}System.out.println();//Iterator迭代器反向遍历//因为反向遍历是从后向前遍历,所以需要知道元素个数ListIterator<Integer> it2 = list.listIterator(list.size());while(it2.hasPrevious()) {System.out.print(it2.previous()+" ");}System.out.println();}

在这里插入图片描述

💐ArrayList和LinkedList的差异

1、数据结构实现:

​ ArrayList是动态数组的数据结构实现,而LinkedList是双向链表的数据结构实现;

2、随机访问效率:

​ ArrayList比LinkedList在随机访问的时候效率要高,因为LinkedList是线性的数据存储方式,所以需要移 动指针指针从前向后依次查找;

3、增加和删除效率:

​ 在非首尾的增加和删除操作,LinkedList要比ArrayList效率要高,因为ArrayList增删操作时,会导致其他下标的元素进行移动,时间复杂度高;

4、内存空间占用:

​ LinkedList比ArrayList更占用空间,因为LinkedList的节点处了存储数据,还存储了两个引用,一个指向前一个元素,一个指向后一个元素;

5、线程安全:

​ ArrayList 和 LinkedList都是不同步的,也就是不保证线程安全;

6、综合来说:

​ 在需要频繁读取聚合中的元素时,更推荐使用ArrayList,而在插入和删除操作较多时,推荐使用LinkedList;

**7、LinkedList的双向链表:**它的每一个节点中都有两个指针,分别指向前驱和后继,所以,从双向链表的任意一个节点开始,都可以很方便的访问它的前驱节点和后继节点;

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

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

相关文章

logback/log4j基本配置和标签详解

什么是logback logback 继承自 log4j&#xff0c;它建立在有十年工业经验的日志系统之上。它比其它所有的日志系统更快并且更小&#xff0c;包含了许多独特并且有用的特性。 logback.xml 首先直接上配置&#xff0c;我在项目过程中发现一些同时遇到需要logback文件的时候就去…

怎样建立一个班级查分系统?

在现代教育中&#xff0c;建立一个高效的班级查分系统对于老师和家长们来说至关重要。物种草作为一款功能强大的在线教育工具&#xff0c;为教师们提供了一个便捷的方式来管理和分享学生成绩。本文将以物种草的口吻&#xff0c;为你介绍如何建立一个高效的班级查分系统&#xf…

SpringBoot2.0入门(详细文档)

文章目录 Springboot是什么Springboot2.x依赖环境和版本新特性说明为什么学习Springboot从springboot优点来看从未来发展的趋势来看 开发环境Spring Boot开发环境搭建和项目启动jdk 的配置Spring Boot 工程的构建maven配置IDEA 快速构建maven 创建工程常用注解 完整代码 Spring…

2023高教社杯数学建模B题思路代码 - 多波束测线问题

# 1 赛题 B 题 多波束测线问题 单波束测深是利用声波在水中的传播特性来测量水体深度的技术。声波在均匀介质中作匀 速直线传播&#xff0c; 在不同界面上产生反射&#xff0c; 利用这一原理&#xff0c;从测量船换能器垂直向海底发射声波信 号&#xff0c;并记录从声波发射到…

使用融云 CallPlus SDK,一小时实现一款 1V1 视频应用

9 月 21 日&#xff0c;融云直播课 社交泛娱乐出海最短变现路径如何快速实现一款 1V1 视频应用&#xff1f; 欢迎点击小程序报名~ 1V1 音视频、远程服务类应用的实现利器——融云 CallPlus SDK 上线&#xff01; 关注【融云全球互联网通信云】了解更多 作为新一代音视频通话场…

基于Python和mysql开发的看图猜成语微信小程序(源码+数据库+程序配置说明书+程序使用说明书)

一、项目简介 本项目是一套基于Python和mysql开发的看图猜成语微信小程序&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Python学习者。 包含&#xff1a;项目源码、项目文档、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都…

2022年全国研究生数学建模竞赛华为杯A题移动场景超分辨定位问题求解全过程文档及程序

2022年全国研究生数学建模竞赛华为杯 A题 移动场景超分辨定位问题 原题再现&#xff1a; 在日常家庭生活中&#xff0c;人们可能需要花费大量时间去寻找随意摆放在家中某些角落里的小物品。但如果给某些重要物品贴上电路标签&#xff0c;再利用诸如扫地机器人的全屋覆盖能力&…

前端实现页面通过canvas添加全屏水印

写在前面&#xff0c;博主是个在北京打拼的码农&#xff0c;从事前端工作5年了&#xff0c;做过十多个大大小小不同类型的项目&#xff0c;最近心血来潮在这儿写点东西&#xff0c;欢迎大家多多指教。 对于文章中出现的任何错误请大家批评指出&#xff0c;一定及时修改。有任何…

视频直播点播平台EasyDSS如何单独保存录像计划文件?具体如何操作呢?

视频推拉流EasyDSS视频直播点播平台&#xff0c;集视频直播、点播、转码、管理、录像、检索、时移回看等功能于一体&#xff0c;可提供音视频采集、视频推拉流、播放H.265编码视频、存储、分发等视频能力服务。 有用户反馈&#xff1a;在视频直播点播平台EasyDSS中设置了片段形…

【数据结构--二叉树】合并二叉树

/*** Definition for a binary tree node.* struct TreeNode {* int val;* struct TreeNode *left;* struct TreeNode *right;* };*/struct TreeNode* mergeTrees(struct TreeNode* root1, struct TreeNode* root2){if(root1NULL&&root2NULL)//两个二叉树都…

华为云云耀云服务器L实例评测|Linux系统之安装Tomcat

华为云云耀云服务器L实例评测&#xff5c;Linux系统之安装Tomcat 一、云耀云服务器L实例介绍1.1 云耀云服务器L实例简介1.2 云耀云服务器L实例特点 二、Tomcat介绍2.1 Tomcat简介2.2 Tomcat特点 三、本次实践介绍3.1 本次实践简介3.2 本次环境规划 四、购买云耀云服务器L实例4.…

risc-v dv源代码分析

地址为 GitHub - chipsalliance/riscv-dv: Random instruction generator for RISC-V processor verificationRandom instruction generator for RISC-V processor verification - GitHub - chipsalliance/riscv-dv: Random instruction generator for RISC-V processor verif…

Windows驱动开发(一)

1. 引言 很难为术语 “驱动程序”提供一个精确的定义。 就最基本的意义而言&#xff0c;驱动程序是一个软件组件&#xff0c;可让操作系统和设备彼此通信。 例如&#xff0c;假设应用程序需要从设备中读取某些数据。 应用程序会调用由操作系统实现的函数&#xff0c;操作系统…

Windows下Git Bash的基本使用

创建版本库 git init 初始化完成后&#xff0c;会在目录下创建一个.git的隐藏目录&#xff0c;用来存放项目信息。 、 添加文件到版本库 在项目目录下新建文件readme.txt&#xff0c;内容为 Git is a version control system Git is a free software This is my first Try …

MySQL知识笔记——初级基础(实施工程师和DBA工作笔记)

老生长谈&#xff0c;MySQL具有开源、支持多语言、性能好、安全性高的特点&#xff0c;广受业界欢迎。 在数据爆炸式增长的年代&#xff0c;掌握一种数据库能够更好的提升自己的业务能力&#xff08;实施工程师&#xff09;。 此系列将会记录我学习和进阶SQL路上的知识&#xf…

Haproxy搭建Web群集

常见的Web集群调度器分为软件和硬件 软件通常使用开源的LVS、Haproxy、Nginx。 * LVS 性能最好&#xff0c;但是搭建相对复杂 * Nginx的upstream模块支持群集功能&#xff0c;但是对群集节点健康检查功能不强&#xff0c;高并发性能没有Haproxy好。 硬件一般使用比较多的是F5、…

postman连接websocket 测试(v8.5.1)

1. postman v8.5版本 以上支持 websocket。 2. 选择websocket请求模块File - New... 3. 输入请求地址, ws:// 控制台输出: 2023-09-12 15:29:23.039 INFO 11592 --- [nio-8080-exec-2] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet di…

sql注入之高权限注入和文件读写

死在山野的风里&#xff0c;活在自由的梦里 sql注入之高权限注入 高权限注入1.多个网站共享mysql服务器2.MySQL 权限介绍3.注入流程查询所有数据库名称查询表名对应的字段名查询数据 文件读写1.文件读写注入的原理2.文件读写注入的条件3.读取文件4.写入文件 高权限注入 在数据…

学习SpringMvc第三战-利用SpringMvc实现CRUD

目录 一.前期环境搭建 1.替换pom.xml的内容 2.导入配置文件(小编上传资源) 3.修改xml文件 4.点击创建自动生成代码 5.写一个类用于处理页面跳转 二.正式启动SpringMVC的CRUD 1.建立接口&#xff0c;调用自动生成的接口 2.构建分页代码 2.1书写BookMapper.xml中分页的方…

12个最受欢迎的3D打印机械臂【开源|DIY|套件】

推荐&#xff1a;用 NSDT编辑器 快速搭建可编程3D场景 机器人手臂的用途各不相同&#xff0c;但大多数都能够执行拾取和放置任务&#xff0c;而有些则配备用于 CNC 工作、激光雕刻&#xff0c;甚至 3D 打印。 机械臂具有广泛的应用和各个领域&#xff0c;从执行精密手术和进行工…