【数据结构】链表(一)

链表(一)

文章目录

  • 链表(一)
    • 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 不是curwhile (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.hqwc.cn/news/64054.html

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

相关文章

Nginx负载均衡搭建

目录 1、准备一台装有nginx服务的主机 2、所需模块说明&#xff1a; 3、两台Web服务器主机 4、 修改nginx的配置文件 5、查看结果&#xff1a; 1、准备一台装有nginx服务的主机 LVS—DR集群的搭建_.98℃的博客-CSDN博客 2、所需模块说明&#xff1a; Nginx http 功能模…

Java算法_ 房子强盗(LeetCode_Hot100)

题目描述&#xff1a;你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统&#xff0c;如果两间相邻的房屋在同一晚上被小偷闯入&#xff0c;系统会自动报警。 给定一个代表…

Golang通过alibabaCanal订阅MySQLbinlog

最近在做redis和MySQL的缓存一致性&#xff0c;一个方式是订阅MySQL的BinLog文件&#xff0c;我们使用阿里巴巴的Canal的中间件来做。 Canal是服务端和客户端两部分构成&#xff0c;我们需要先启动Canal的服务端&#xff0c;然后在Go程序里面连接Canal服务端&#xff0c;即可监…

【2023最新美团笔试题目分析】“求最多出现数字及次数“、坦克大战在线对战游戏(问题描述 + 示例代码 + 时间复杂度分析)

弃幼少嬉戏堕慢之心,而衎衎于进德修业之志。 🎯作者主页: 追光者♂🔥 🌸个人简介: 💖[1] 计算机专业硕士研究生💖 🌿[2] 2023年城市之星领跑者TOP1(哈尔滨)🌿 🌟[3] 2022年度博客之星人工智能领域TOP4🌟 🏅[4] 阿里云社区特邀专家博主

无涯教程-Perl - mkdir函数

描述 此功能使用MODE指定的模式创建一个名称和路径EXPR的目录,为清楚起见,应将其作为八进制值提供。 语法 以下是此函数的简单语法- mkdir EXPR,MODE返回值 如果失败,此函数返回0,如果成功,则返回1。 例 以下是显示其基本用法的示例代码- #!/usr/bin/perl -w$dirname &…

python爬虫之scrapy框架介绍

一、Scrapy框架简介 Scrapy 是一个开源的 Python 库和框架&#xff0c;用于从网站上提取数据。它为自从网站爬取数据而设计&#xff0c;也可以用于数据挖掘和信息处理。Scrapy 可以从互联网上自动爬取数据&#xff0c;并将其存储在本地或在 Internet 上进行处理。Scrapy 的目标…

28 | Boss直聘数据分析

针对boss直聘网的招聘信息,然后分析互联网发展排名前十的城市在互联网方面职位的薪水,学历要求,经验要求,等等信息。 准备从以下几个方面进行分析: (1)各个城市的平均工资 (2)各个学历的平均工资 (3)各个岗位的平均工资 (4)不同工作经验要求的工资 (5)各个经验…

天津大数据培训机构哪家好?大数据必备知识

随着我国互联网IT行业的发展&#xff0c;我们步入了大数据时代&#xff0c;现在市场上急需大量的大数据专业人才&#xff0c;发展空间大&#xff0c;从业范围广&#xff0c;学习大数据专业对未来还是很有帮助的。今天小编来带大家了解一下大数据的知识和学习方式&#xff0c;作…

设计模式行为型——状态模式

目录 状态模式的定义 状态模式的实现 状态模式角色 状态模式类图 状态模式举例 状态模式代码实现 状态模式的特点 优点 缺点 使用场景 注意事项 实际应用 在软件开发过程中&#xff0c;应用程序中的部分对象可能会根据不同的情况做出不同的行为&#xff0c;把这种对…

4个简化IT服务台任务的ChatGPT功能

最近几个月&#xff0c;ChatGPT 风靡全球&#xff0c;这是一个 AI 聊天机器人&#xff0c;使用户能够生成脚本、文章、锻炼图表等。这项技术在各行各业都有无穷无尽的应用&#xff0c;在本文中&#xff0c;我们将研究这种现代技术如何帮助服务台团队增强服务交付和客户体验。 什…

优秀的 Modbus 从站(从机、服务端)仿真器、串口调试工具

文章目录 优秀的 Modbus 从站&#xff08;从机、服务端&#xff09;仿真器、串口调试工具主要功能软件截图 优秀的 Modbus 从站&#xff08;从机、服务端&#xff09;仿真器、串口调试工具 官网下载地址&#xff1a;http://www.redisant.cn/mse 主要功能 支持多种Modbus协议…

EVE-NG 镜像导入

目录 1.文件构成 2.导入锐捷镜像 2.1 上传模板 2.2 上传图标 2.3上传启动镜像 2.4上传配置脚本 2.5 修复文件权限 2.6 查看​ 1.文件构成 eve-ng 镜像由4个文件构成 1.启动镜像&#xff1a;运行时加载的磁盘镜像文件&#xff1b; 2.图标&#xff1a;在拓扑中显示的图…