Java链表(2)
🐵本篇文章将对双向链表进行讲解,模拟实现双向链表的常用方法
一、什么是双向链表
双向链表在指针域上相较于单链表,每一个节点多了一个指向前驱节点的引用prev以及多了指向最后一个节点的引用last:
二、双向链表的模拟实现
首先将要模拟实现的方法写到IList接口中:
public interface IList {//头插法插入节点public void addFirst(int data);//尾插法插入节点public void addLast(int data);//任意位置插入,第一个数据节点为0号下标public void addIndex(int index,int data);//查找是否包含关键字key是否在链表当中public boolean contains(int key);//删除第一次出现关键字为key的节点public void remove(int key);//删除所有值为key的节点public void removeAllKey(int key);//得到链表的长度public int size();//清除链表public void clear();//显示链表public void display();}
之后再创建一个MyLinkedList类实现上述接口并重写接口中所有的方法
public class MySingleList implements IList{static class ListNode {public int val;public ListNode next;public ListNode prev;public ListNode(int val) {this.val = val;}}public ListNode head; //指向第一个节点public ListNode last; //指向最后一个节点/*以下是要重写IList接口中的方法*/...}
2.1 模拟实现
public void addFirst(int data);
双向链表的头插法和单链表基本一致,只不过当链表为空时不仅要让head指向新节点还要让last也指向新节点
public void addFirst(int data) {ListNode newNode = new ListNode(data);if (head == null) {head = newNode;last = newNode;return;}newNode.next = head;head.prev = newNode;head = newNode;}
public void addLast(int data);
当链表为空时要让head和last都指向新节点,当链表部不为空时要让最后一个节点的next指向新节点,之后让新节点的prev指向原来的最后一个节点
public void addLast(int data) {ListNode newNode = new ListNode(data);if (head == null) {head = newNode;last = newNode;return;}last.next = newNode;newNode.prev = last;last = newNode;}
public void addLast(int data);
在任意位置处插入一个节点,第一个节点的索引为0
首先要判断一下index是否合法:
public class IndexException extends RuntimeException{public IndexException(String message) {super(message);}
}=======================================================public void addIndex(int index, int data) {if (index < 0 || index > size()) { //size()为链表长度throw new IndexException("下标错误");}...
}
在任意位置插入节点,所以如果index为0或等于链表长度,就可以直接使用刚刚实现过的头插和尾插方法
public void addIndex(int index, int data) {if (index < 0 || index > size()) {throw new IndexException("下标错误");}if (index == 0) {addFirst(data);}if (index == size()) {addLast(data);}...
}
一般情况下(在中间插入),单链表中要通过循环找到插入位置的前一个节点,但在双向链表中,直接循环到插入位置(插入位置记为cur)即可
public void addIndex(int index, int data) {if (index < 0 || index > size()) {throw new IndexException("下标错误");}if (index == 0) {addFirst(data);return;}if (index == size()) {addLast(data);return;}ListNode newNode = new ListNode(data);ListNode cur = head;for (int i = 0; i < index; i++) {cur = cur.next;}/*一般情况下*/newNode.next = cur;cur.prev.next = newNode;newNode.prev = cur.prev;cur.prev = newNode;}
public void remove(int key);
删除第一次出现val = key的节点
先考虑常规情况,即通过遍历找到要删除的节点,这里记为cur
让cur的前驱节点的next指向cur的后继节点,cur的后继节点的prev指向cur的前驱节点
public void remove(int key) {ListNode cur = head;while(cur != null) {if (cur.val == key) {cur.prev.next = cur.next;cur.next.prev = cur.prev;return;}cur = cur.next;}}
之后有两种特殊情况需要考虑:1.cur为第一个节点;2.cur为最后一个节点;当cur为这两种情况时如果使用上述代码,会引发空指针异常,所以这两种情况要单独考虑
1.cur为第一个节点:此时需要让cur的后继节点prev指向空(cur.prev = null),并让head = head.next,但是这样还有一个小问题:当链表中只有一个节点时也会引发空指针异常,这个问题也要单独处理,只需要直接让head = null即可
if (cur == head) {head = head.next;if (head == null) {last = null;} else {head.prev = null;}return;
}
2.cur为最后一个节点:只需让cur的前驱节点的next指向空,并让last = last.prev;即可
if (cur.next == null) {cur.prev.next = null;last = last.prev;return;
}
remove的最终代码如下:
public void remove(int key) {ListNode cur = head;while(cur != null) {if (cur.val == key) {if (cur == head) {head = head.next;if (head == null) {last = null;} else {head.prev = null;}return;}if (cur.next == null) {cur.prev.next = null;last = last.prev;return;}cur.prev.next = cur.next;cur.next.prev = cur.prev;return;}cur = cur.next;}}
void removeAllKey(int key);
删除所有val = key的节点,这里只需要将remove方法修改以下即可
public void removeAllKey(int key) {ListNode cur = head;while(cur != null) {if (cur.val == key) {if (cur == head) {head = head.next;if (head == null) {last = null;} else {head.prev = null;}cur = head;continue;}if (cur.next == null) {cur.prev.next = null;last = last.prev;break;}cur.prev.next = cur.next;cur.next.prev = cur.prev;}cur = cur.next;}}
剩下的contains() 、size()、clear()、display()方法和上篇文章的单链表实现方法一致
三、LinkedList类讲解
LinkedList类是Java提供的类,底层是一个双向链表,包含我们刚刚实现过的方法,LinkedList也实现了List接口
3.1 LinkedList构造方法
LinkedList有两个构造方法:
1.无参构造方法
public LinkedList()
2. 带参构造方法
public LinkedList(Collection<? extends E> c)
该构造方法可以将c构造为双向链表,前提是c实现了Collection接口并且其泛型必须是E或是E的子类,例如:
ArrayList<Integer> list1 = new ArrayList<>();
list1.add(1);
list1.add(2);LinkedList<Integer> list = new LinkedList<>(list1); //list1属于ArrayList类,实现了Collection接口,泛型和list1一样都是Integer,此时顺序表list1就被构造为了双向链表list
3.2 LinkedList类常用方法
boolean add(E e) //尾插 evoid add(int index, E element) //将 e 插入到 index 位置boolean addAll(Collection<? extends E> c) //尾插 c 中的元素E remove(int index) //删除 index 位置元素boolean remove(Object o) //删除遇到的第一个 oE get(int index) //获取下标 index 位置元素E set(int index, E element) //将下标 index 位置元素设置为 elementvoid clear() //清空链表boolean contains(Object o) //判断 o 是否在线性表中int indexOf(Object o) //返回第一个 o 所在下标int lastIndexOf(Object o) //返回最后一个 o 的下标List<E> subList(int fromIndex, int toIndex) //截取链表,按左闭右开的区间截取[fromIndex, toIndex)
这些方法的底层实现方式和我们上述模拟实现的方法的实现方式相同
3.3 LinkedList遍历
1. for循环
for (int i = 0; i < list.size(); i++) {System.out.print(list.get(i) +" ");
}
2. for-each
for (int x : list) {System.out.print(x +" ");
}
3.迭代器
顺向遍历
ListIterator<Integer> it = list.listIterator();
while(it.hasNext()) {System.out.print(it.next() +" ");
}
逆向遍历
ListIterator<Integer> it1 = list.listIterator(list.size());
while(it1.hasPrevious()) {System.out.print(it1.previous() +" ");
}
🙉本篇文章到此结束,下篇文章会对栈相关知识进行讲解
相关文章:

Java链表(2)
🐵本篇文章将对双向链表进行讲解,模拟实现双向链表的常用方法 一、什么是双向链表 双向链表在指针域上相较于单链表,每一个节点多了一个指向前驱节点的引用prev以及多了指向最后一个节点的引用last: 二、双向链表的模拟实现 首先…...

Python qt.qpa.xcb: could not connect to display解决办法
遇到问题:qt.qpa.xcb: could not connect to display 解决办法,在命令行输入: export DISPLAY:0 然后重新跑python程序,解决! 参考博客:qt.qpa.xcb: could not connect to displayqt.qpa.plugin: Could …...
Compose | UI组件(八) | Dialog - 对话框
文章目录 前言Dialog 普通弹框Dialog 普通弹框的使用AlertDialog 警告弹框AlertDialog 警告弹框的使用 总结 前言 在我们传统的UI界面中,经常用到弹框,Compose也有弹框,但是Compose的弹框显示和隐藏和传统的弹框显示(show&#x…...
【Spark系列6】如何做SQL查询优化和执行计划分析
Apache Spark SQL 使用 Catalyst 优化器来生成逻辑执行计划和物理执行计划。逻辑执行计划描述了逻辑上如何执行查询,而物理执行计划则是 Spark 实际执行的步骤。 一、查询优化 示例 1:过滤提前 未优化的查询 val salesData spark.read.parquet(&quo…...

Observability:在 Elastic Stack 8.12 中使用 Elastic Agent 性能预设
作者:来自 Elastic Nima Rezainia, Bill Easton 8.12 中 Elastic Agent 性能有了重大改进 最新版本 8.12 标志着 Elastic Agent 和 Beats 调整方面的重大转变。 在此更新中,Elastic 引入了 Performance Presets,旨在简化用户的调整过程并增强…...

空间数据分析和空间统计工具库PySAL入门
空间数据分析是指利用地理信息系统(GIS)技术和空间统计学等方法,对空间数据进行处理、分析和可视化,以揭示数据之间的空间关系和趋势性,为决策者提供有效的空间决策支持。空间数据分析已经被广泛运用在城市规划、交通管理、环境保护、农业种植…...

LabVIEW电液伺服控制系统
介绍了如何利用ARM微处理器和LabVIEW软件开发一个高效、精准的电液伺服控制系统。通过结合这两种技术,我们能够提高系统的数字化程度、集成化水平,以及控制精度,从而应对传统电液伺服控制器面临的问题。 该电液伺服控制系统由多个关键部分组…...
Dubbo_入门
系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 Dubbo_入门 提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 系列文章目录前言一、什么是分布式系统二、什么…...

Ubuntu22.04更换软件源
本文以Ubuntu22.04更换科大源为例演示更改软件源的方法,其他版本的Ubuntu系统或更换其他软件源,如清华源,阿里源等,方法类似。 前言 中国科学技术大学开源软件镜像由中国科学技术大学网络信息中心提供支持。 mirrors.ustc.edu.…...
解密Android某信聊天记录
前置条件 frida, frida-tools, adb 获取密码 h.js console.log(script loaded successfully);function xx() {function strf(str, replacements) {return str.replace(/\$\{\w\}/g, function(placeholderWithDelimiters) {var placeholderWithoutDelimiters placeholderWi…...

海外云手机对于亚马逊卖家的作用
近年来,海外云手机作为一种新型模式迅速崭露头角,成为专业的出海SaaS平台软件。海外云手机在云端运行和存储数据,通过网页端操作,将手机芯片放置在机房,通过网络连接到服务器,为用户提供便捷的上网功能。因…...
交换机的发展历史
交换机发展历史是什么,详细介绍每代交换机的性能特点,特色功能 交换机的发展历史可以大致分为以下几个阶段,每个阶段的设备性能特点和特色功能有所差异: 1. 第一代以太网交换机(1980年代末至1990年代初) …...

用katalon解决接口/自动化测试拦路虎--参数化
不管是做接口测试还是做自动化测试,参数化肯定是一个绕不过去的坎。 因为我们要考虑到多个接口都使用相同参数的问题。所以,本文将讲述一下katalon是如何进行参数化的。 全局变量 右侧菜单栏中打开profile,点击default,打开之后…...

CSRF靶场练习
简述:CSRF漏洞实际很少;条件限制很多;局限性很大;实验仅供参考,熟悉csrf概念和攻击原理即可 Pikachu靶场 CSRF GET 登录用户vince的账户可以看到用户的相关信息; 点击修改个人信息,发现数据包…...
pgsql的查询语句有没有走索引
使用EXPLAIN ANALYZE命令: EXPLAIN ANALYZE [ ( option [, ...] ) ]statement示例: EXPLAIN ANALYZE SELECT * FROM employees WHERE age > 30;在执行计划中,如果看到索引扫描(Index Scan)或位图堆扫描࿰…...

jenkins部署(docker)
docker部署,避免安装tomcat 1.拉镜像 docker pull jenkins/jenkins2.宿主机创建文件夹 mkdir -p /lzp/jenkins_home chmod 777 /lzp/jenkins_home/3.启动容器 docker run -d -p 49001:8080 -p 49000:50000 --privilegedtrue -v /lzp/jenkins_home:/var/jenkins_…...

Python实现时间序列分析AR定阶自回归模型(ar_select_order算法)项目实战
说明:这是一个机器学习实战项目(附带数据代码文档视频讲解),如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 时间序列分析中,AR定阶自回归模型(AR order selection)是指确定自回…...

Springboot自定义线程池实现多线程任务
1. 在启动类添加EnableAsync注解 2.自定义线程池 package com.bt.springboot.config;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.concurrent.ThreadPoolTask…...

linux离线升级openssh方法
检查openssh版本: 升级前openssh 版本为7.4 openssl 版本为1.0.2k Openssh9.6 所需openssl >1.1.1 因此openssl也需要升级。 为了防止升级失败,无法使用SSH登录,首先安装telnet 预防。查看是否安装了telnet 客户端及服务 未安装tel…...

(五)MySQL的备份及恢复
1、MySQL日志管理 在数据库保存数据时,有时候不可避免会出现数据丢失或者被破坏,这样情况下,我们必须保证数据的安全性和完整性,就需要使用日志来查看或者恢复数据了 数据库中数据丢失或被破坏可能原因: 误删除数据…...
【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15
缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下: struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...

【WiFi帧结构】
文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成:MAC头部frame bodyFCS,其中MAC是固定格式的,frame body是可变长度。 MAC头部有frame control,duration,address1,address2,addre…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)
2025年能源电力系统与流体力学国际会议(EPSFD 2025)将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会,EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

视觉slam十四讲实践部分记录——ch2、ch3
ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...

人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...
在 Spring Boot 项目里,MYSQL中json类型字段使用
前言: 因为程序特殊需求导致,需要mysql数据库存储json类型数据,因此记录一下使用流程 1.java实体中新增字段 private List<User> users 2.增加mybatis-plus注解 TableField(typeHandler FastjsonTypeHandler.class) private Lis…...

Neko虚拟浏览器远程协作方案:Docker+内网穿透技术部署实践
前言:本文将向开发者介绍一款创新性协作工具——Neko虚拟浏览器。在数字化协作场景中,跨地域的团队常需面对实时共享屏幕、协同编辑文档等需求。通过本指南,你将掌握在Ubuntu系统中使用容器化技术部署该工具的具体方案,并结合内网…...

Windows电脑能装鸿蒙吗_Windows电脑体验鸿蒙电脑操作系统教程
鸿蒙电脑版操作系统来了,很多小伙伴想体验鸿蒙电脑版操作系统,可惜,鸿蒙系统并不支持你正在使用的传统的电脑来安装。不过可以通过可以使用华为官方提供的虚拟机,来体验大家心心念念的鸿蒙系统啦!注意:虚拟…...
ThreadLocal 源码
ThreadLocal 源码 此类提供线程局部变量。这些变量不同于它们的普通对应物,因为每个访问一个线程局部变量的线程(通过其 get 或 set 方法)都有自己独立初始化的变量副本。ThreadLocal 实例通常是类中的私有静态字段,这些类希望将…...
Java并发编程实战 Day 11:并发设计模式
【Java并发编程实战 Day 11】并发设计模式 开篇 这是"Java并发编程实战"系列的第11天,今天我们聚焦于并发设计模式。并发设计模式是解决多线程环境下常见问题的经典解决方案,它们不仅提供了优雅的设计思路,还能显著提升系统的性能…...