Collection与数据结构 链表与LinkedList (一):链表概述与单向无头非循环链表实现

1.ArrayList的缺点

上篇文章我们已经对顺序表进行了实现,并且对ArrayList进行了使用,我们知道ArrayList底层是使用数组实现的.
由于其底层是一段连续空间,当在ArrayList任意位置插入或者删除元素时,就需要将后序元素整体往前或者往后搬移,时间复杂度为O(n),效率比较低,因此ArrayList不适合做任意位置插入和删除比较多的场景。因此:java集合中又引入了LinkedList,即链表结构。

2.链表

2.1 链表的概念与结构

链表是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的.就像一列动车一样.
在这里插入图片描述
在这里插入图片描述
通过上图我们可以看到,一个链表中有一节一节的"车厢",还有中间的"链条".我们称每一节"车厢"为结点,我们可以看到每一个结点中有下一个结点的地址,链表就是通过存储节点的地址来连接起来的,还有该结点的值.上面就是我们需要重点掌握的一种链表类型,叫做单向无头非循环链表.其实链表还有好多类型,下面我们来展示.

2.2 链表的类型

链表有以下几种性质:有头/无头,单向/双向,循环/非循环

  1. 有头/无头
    在这里插入图片描述
    在这里插入图片描述
    它们的区别就是,有头链表的head永远指向一个固定的结点,而无头链表的head永远在改变.
  2. 单向/双向
    在这里插入图片描述

在这里插入图片描述
3. 循环/非循环
在这里插入图片描述
在这里插入图片描述
上面几种性质排列组合可以得到许多中链表的类型,这里我们重点掌握2中即可,一种是单向无头非循环,它在笔面试中经常出现,另一种是是无头双向循环链表,Java中的LinkedList底层就是通过它来实现的.

3. 单向无头非循环链表实现

下面是要实现的接口,即链表中的常用方法:

public interface ILinkedList {void addFirst(int data);//尾插法void addLast(int data);//任意位置插入,第一个数据节点为0号下标void addIndex(int index,int data);//查找是否包含关键字key是否在单链表当中boolean contains(int key);//删除第一次出现关键字为key的节点void remove(int key);//删除所有值为key的节点void removeAllKey(int key);//得到单链表的长度int size();//清空链表void clear() ;//打印链表void display();}

下面我们来实现这些方法:

public class MyLinkedList implements ILinkedList {static class Node{public int val;public Node next = null;public Node(int val) {this.val = val;}}public Node head = null;/*** 创建默认链表*/public void createDeafultLinkedList(){Node node = new Node(23);this.head = node;Node node1 = new Node(34);node.next = node1;Node node2 = new Node(45);node1.next = node2;Node node3 = new Node(56);node2.next = node3;Node node4 = new Node(67);node3.next = node4;}/*** 在链表头部添加新的结点* @param data*/@Overridepublic void addFirst(int data) {Node node = new Node(data);
//        if(head == null){
//            head = node;
//        }else {
//            node.next = head;
//            head = node;
//        }//上面的代码其实没必要验证链表是否为空,head为null赋值过去还是nullnode.next = this.head;this.head = node;}/*** 在尾部添加新的结点* @param data*/@Overridepublic void addLast(int data) {Node node = new Node(data);Node cur = this.head;if (this.head == null){this.head = node;}else {while (cur.next != null){cur = cur.next;}cur.next = node;}}/*** 在指定位置添加结点* @param index* @param data*/@Overridepublic void addIndex(int index, int data) {if (index > size() || index < 0){throw new IndexExeption("下标有误");}Node node = new Node(data);if (this.head == null){this.head = node;return;//记得返回,不然会执行中间插入的代码}if (index == 0){//在链表头添加addFirst(node.val);return;}if (index == size()){//在链表尾部添加addLast(node.val);return;}//在中部添加Node cur = this.head;int count = 0;while (count != index-1){//寻找cur的前一个结点cur = cur.next;count++;}node.next = cur.next;cur.next = node;}/*** 检测链表中是否含有指定的值* @param key* @return*/@Overridepublic boolean contains(int key) {Node cur = this.head;while (cur != null){//先遍历链表if(cur.val == key){//在遍历的过程中如果等于,返回truereturn true;}cur = cur.next;}return false;}/*** 移除遇到的第一个值为key的元素* @param key*/@Overridepublic void remove(int key) {if (this.head == null){return;}if (head.val == key){head = head.next;return;}Node cur = findPreNode(key);//需要找到前一个结点才可以删除if (cur == null){//没找到要删除的结点return;}cur.next = cur.next.next;}private Node findPreNode(int key){//找到要删除结点的前一个结点Node cur = this.head;while (cur.next != null){//这里必须写成next不等于null,否则下面可能会出现空指针异常if (cur.next.val == key){return cur;}cur = cur.next;}return null;}/*** 移除所有符合值为key的结点* @param key*/@Overridepublic void removeAllKey(int key) {if (this.head == null){return;}Node pre = this.head;Node cur = this.head.next;//先不要管头结点,先删中间的while(cur != null){if (cur.val == key){pre.next = cur.next;}else {pre = pre.next;//若该结点不是要删除的结点,pre往后走}cur = cur.next;}//所有的都删完了,删除头结点if (head.val == key){head = head.next;}}/*** 计算链表大小* @return*/@Overridepublic int size() {int count = 0;Node cur = this.head;while (cur != null){count++;cur = cur.next;}return count;}/*** 清空链表*/@Overridepublic void clear() {head = null;//为被引用的结点会被JWM回收}/*** 打印链表*/@Overridepublic void display() {Node cur = this.head;while (cur != null){System.out.print(cur.val+" ");cur = cur.next;}System.out.println();}/*** 从指定位置开始打印链表* @param node*/public void display(Node node) {Node cur = node;while (cur != null){System.out.print(cur.val+" ");cur = cur.next;}System.out.println();}}

上面有几个问题需要注意

  1. 在中间插入元素的时候先要进行后链接,在进行前链接,如果先进行前链接,cur的下一个结点的地址就会丢失
  2. 在删除所有值为key的结点的时候,先删除head后符合条件的结点最后再处理head.

下面是对上述问题的动态演示,储存在个人gitee中:
插入结点错误演示
插入结点正确演示
删除所有key结点

下面我们对上述实现的方法进行测试:

*** 开始测试*/
public class Test {public static void main(String[] args) {MyLinkedList myLinkedList = new MyLinkedList();myLinkedList.createDeafultLinkedList();myLinkedList.addFirst(11);myLinkedList.addLast(88);myLinkedList.display();System.out.println(myLinkedList.size());myLinkedList.addIndex(2,33);myLinkedList.display();System.out.println(myLinkedList.contains(11));System.out.println(myLinkedList.contains(12));myLinkedList.addIndex(1,23);myLinkedList.addIndex(1,23);myLinkedList.addIndex(1,23);myLinkedList.display();myLinkedList.remove(23);myLinkedList.display();myLinkedList.removeAllKey(23);myLinkedList.display();myLinkedList.clear();myLinkedList.display();}
}

测试结果:
在这里插入图片描述

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

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

相关文章

SpringMvc之映射器HandlerMapping

简介 在springmvc的处理流程中&#xff0c;第一步就是查询请求对应的映射器&#xff0c;然后组装成处理器链处理请求&#xff0c;本文意在梳理该过程 重要实现 HandlerMapping是一个接口&#xff0c;该接口用于通过HttpServletRequest寻找对应的处理器&#xff0c;接口介绍如下…

windows系统安装RabbitMQ

RabbitMQ是实现了高级消息队列协议&#xff08;AMQP&#xff09;的开源消息代理软件&#xff08;亦称面向消息的中间件&#xff09;。RabbitMQ服务器是用Erlang语言编写的&#xff0c;而集群和故障转移是构建在开放电信平台框架上的。所有主要的编程语言均有与代理接口通讯的客…

PL/SQL概述

oracle从入门到总裁:​​​​​​https://blog.csdn.net/weixin_67859959/article/details/135209645 PL/SQL概述 PL/SQL(Procedural Language extension to SQL)是 Oracle 对标准 SQL语言的扩充&#xff0c;是专门用于各种环境下对 Oracle 数据库进行访问和开发的语言。 由…

【有限状态机】- FSM详细讲解 【附Autoware有限状态机模型代码讲解】

参考博客&#xff1a; &#xff08;1&#xff09;FSM&#xff08;有限状态机&#xff09; &#xff08;2&#xff09;关于有限状态机(FSM)的一些思考 &#xff08;3&#xff09;状态设计模式 1 状态机简介 有限状态机FSM&#xff1a;有限个状态以及在这些状态之间的转移和动作…

文献阅读工具-->Adobe pdf + 有道词典

Adobe pdf 有道词典 最近一直在考虑用什么文献阅读工具&#xff0c;痛点无非就是想用翻译功能&#xff0c;Adobe pdf的添加注释已经很好用了&#xff0c;使用了zotero&#xff0c;感觉不行&#xff08;不能直接对原文件修改&#xff0c;有副本&#xff0c;麻烦&#xff09;。…

帆软报表在arm架构的linux

有朋友遇到一个问题在部署帆软报表时遇到报错。 问 我在 arm架构的linux服务器上部署帆软报表遇到了一个棘手的问题&#xff0c;你有空帮忙看下嘛。 我看后台日志报的错是 需要升级 gcc、libmawt.so &#xff0c;是系统中缺少Tomcat需要的依赖库&#xff0c;你之前处理过类似…

服务器监控软件夜莺采集监控(三)

文章目录 一、采集器插件1. exec插件2. rabbitmq插件3. elasticsearch插件 二、监控仪表盘1. 系统信息2. 数据服务3. NginxMQ4. Docker5. 业务日志 一、采集器插件 1. exec插件 input.exec/exec.toml [[instances]] commands ["/home/monitor/categraf/scripts/*.sh&q…

6.二叉树——2.重建树

已知先序和中序序列 根据先序序列找到树根根据树根和中序序列找到左右子树 同理根据后序序列和中序序列也能重构树&#xff0c;但前序和后序不可以 递归coding思路 设先序序列为preorder[n]&#xff0c;中序序列为midorder[n] 大事化小&#xff1a; 确定根&#xff0c;即树…

uniapp写小程序如何实现分包

众所众知小程序上传的过程中对包的大小有限制&#xff0c;正常情况下不允许当个包超过2M&#xff0c;所以需要分包 需要再pages.json这个文件夹中进行配置 "pages": [{"path": "pages/index/index","style": {"navigationBarTit…

基于java实现学科竞赛管理系统【Springboot+mybatis+layui】

基于java实现学科竞赛管理系统【Springbootmybatislayui】 博主介绍&#xff1a;多年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 央顺技术团队 Java毕设项目精品实战案例《1000套》 欢迎点赞 收藏 ⭐留言 文…

UMass、MIT等提出3D世界具身基础模型,机器人根据生成的世界模型无缝连接3D感知、推理和行动

在最近的研究中&#xff0c;视觉-语言-动作&#xff08;VLA&#xff0c;vision-language-action&#xff09;模型的输入基本都是2D数据&#xff0c;没有集成更通用的3D物理世界。 此外&#xff0c;现有的模型通过学习「感知到动作的直接映射」来进行动作预测&#xff0c;忽略了…

流量调度平台:优化资源配置,提升用户体验

随着互联网和移动互联网的快速发展&#xff0c;流量调度平台作为一种关键的技术解决方案&#xff0c;正成为各行业提高资源利用率、优化用户体验的重要工具。本文将深入探讨流量调度平台的意义、特点以及在不同领域的应用场景。 ### 什么是流量调度平台&#xff1f; 流量调度…