当前位置: 首页 > news >正文

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)

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

Python qt.qpa.xcb: could not connect to display解决办法

遇到问题&#xff1a;qt.qpa.xcb: could not connect to display 解决办法&#xff0c;在命令行输入&#xff1a; export DISPLAY:0 然后重新跑python程序&#xff0c;解决&#xff01; 参考博客&#xff1a;qt.qpa.xcb: could not connect to displayqt.qpa.plugin: Could …...

Compose | UI组件(八) | Dialog - 对话框

文章目录 前言Dialog 普通弹框Dialog 普通弹框的使用AlertDialog 警告弹框AlertDialog 警告弹框的使用 总结 前言 在我们传统的UI界面中&#xff0c;经常用到弹框&#xff0c;Compose也有弹框&#xff0c;但是Compose的弹框显示和隐藏和传统的弹框显示&#xff08;show&#x…...

【Spark系列6】如何做SQL查询优化和执行计划分析

Apache Spark SQL 使用 Catalyst 优化器来生成逻辑执行计划和物理执行计划。逻辑执行计划描述了逻辑上如何执行查询&#xff0c;而物理执行计划则是 Spark 实际执行的步骤。 一、查询优化 示例 1&#xff1a;过滤提前 未优化的查询 val salesData spark.read.parquet(&quo…...

Observability:在 Elastic Stack 8.12 中使用 Elastic Agent 性能预设

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

空间数据分析和空间统计工具库PySAL入门

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

LabVIEW电液伺服控制系统

介绍了如何利用ARM微处理器和LabVIEW软件开发一个高效、精准的电液伺服控制系统。通过结合这两种技术&#xff0c;我们能够提高系统的数字化程度、集成化水平&#xff0c;以及控制精度&#xff0c;从而应对传统电液伺服控制器面临的问题。 该电液伺服控制系统由多个关键部分组…...

Dubbo_入门

系列文章目录 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 Dubbo_入门 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 系列文章目录前言一、什么是分布式系统二、什么…...

Ubuntu22.04更换软件源

本文以Ubuntu22.04更换科大源为例演示更改软件源的方法&#xff0c;其他版本的Ubuntu系统或更换其他软件源&#xff0c;如清华源&#xff0c;阿里源等&#xff0c;方法类似。 前言 中国科学技术大学开源软件镜像由中国科学技术大学网络信息中心提供支持。 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…...

海外云手机对于亚马逊卖家的作用

近年来&#xff0c;海外云手机作为一种新型模式迅速崭露头角&#xff0c;成为专业的出海SaaS平台软件。海外云手机在云端运行和存储数据&#xff0c;通过网页端操作&#xff0c;将手机芯片放置在机房&#xff0c;通过网络连接到服务器&#xff0c;为用户提供便捷的上网功能。因…...

交换机的发展历史

交换机发展历史是什么&#xff0c;详细介绍每代交换机的性能特点&#xff0c;特色功能 交换机的发展历史可以大致分为以下几个阶段&#xff0c;每个阶段的设备性能特点和特色功能有所差异&#xff1a; 1. 第一代以太网交换机&#xff08;1980年代末至1990年代初&#xff09; …...

用katalon解决接口/自动化测试拦路虎--参数化

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

CSRF靶场练习

简述&#xff1a;CSRF漏洞实际很少&#xff1b;条件限制很多&#xff1b;局限性很大&#xff1b;实验仅供参考&#xff0c;熟悉csrf概念和攻击原理即可 Pikachu靶场 CSRF GET 登录用户vince的账户可以看到用户的相关信息&#xff1b; 点击修改个人信息&#xff0c;发现数据包…...

pgsql的查询语句有没有走索引

使用EXPLAIN ANALYZE命令&#xff1a; EXPLAIN ANALYZE [ ( option [, ...] ) ]statement示例&#xff1a; EXPLAIN ANALYZE SELECT * FROM employees WHERE age > 30;在执行计划中&#xff0c;如果看到索引扫描&#xff08;Index Scan&#xff09;或位图堆扫描&#xff0…...

jenkins部署(docker)

docker部署&#xff0c;避免安装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算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 时间序列分析中&#xff0c;AR定阶自回归模型&#xff08;AR order selection&#xff09;是指确定自回…...

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版本&#xff1a; 升级前openssh 版本为7.4 openssl 版本为1.0.2k Openssh9.6 所需openssl >1.1.1 因此openssl也需要升级。 为了防止升级失败&#xff0c;无法使用SSH登录&#xff0c;首先安装telnet 预防。查看是否安装了telnet 客户端及服务 未安装tel…...

(五)MySQL的备份及恢复

1、MySQL日志管理 在数据库保存数据时&#xff0c;有时候不可避免会出现数据丢失或者被破坏&#xff0c;这样情况下&#xff0c;我们必须保证数据的安全性和完整性&#xff0c;就需要使用日志来查看或者恢复数据了 数据库中数据丢失或被破坏可能原因&#xff1a; 误删除数据…...

深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录

ASP.NET Core 是一个跨平台的开源框架&#xff0c;用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录&#xff0c;以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...

XCTF-web-easyupload

试了试php&#xff0c;php7&#xff0c;pht&#xff0c;phtml等&#xff0c;都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接&#xff0c;得到flag...

Java 语言特性(面试系列1)

一、面向对象编程 1. 封装&#xff08;Encapsulation&#xff09; 定义&#xff1a;将数据&#xff08;属性&#xff09;和操作数据的方法绑定在一起&#xff0c;通过访问控制符&#xff08;private、protected、public&#xff09;隐藏内部实现细节。示例&#xff1a; public …...

oracle与MySQL数据库之间数据同步的技术要点

Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异&#xff0c;它们的数据同步要求既要保持数据的准确性和一致性&#xff0c;又要处理好性能问题。以下是一些主要的技术要点&#xff1a; 数据结构差异 数据类型差异&#xff…...

scikit-learn机器学习

# 同时添加如下代码, 这样每次环境(kernel)启动的时候只要运行下方代码即可: # Also add the following code, # so that every time the environment (kernel) starts, # just run the following code: import sys sys.path.append(/home/aistudio/external-libraries)机…...

tomcat入门

1 tomcat 是什么 apache开发的web服务器可以为java web程序提供运行环境tomcat是一款高效&#xff0c;稳定&#xff0c;易于使用的web服务器tomcathttp服务器Servlet服务器 2 tomcat 目录介绍 -bin #存放tomcat的脚本 -conf #存放tomcat的配置文件 ---catalina.policy #to…...

算法打卡第18天

从中序与后序遍历序列构造二叉树 (力扣106题) 给定两个整数数组 inorder 和 postorder &#xff0c;其中 inorder 是二叉树的中序遍历&#xff0c; postorder 是同一棵树的后序遍历&#xff0c;请你构造并返回这颗 二叉树 。 示例 1: 输入&#xff1a;inorder [9,3,15,20,7…...

Visual Studio Code 扩展

Visual Studio Code 扩展 change-case 大小写转换EmmyLua for VSCode 调试插件Bookmarks 书签 change-case 大小写转换 https://marketplace.visualstudio.com/items?itemNamewmaurer.change-case 选中单词后&#xff0c;命令 changeCase.commands 可预览转换效果 EmmyLua…...

Mac flutter环境搭建

一、下载flutter sdk 制作 Android 应用 | Flutter 中文文档 - Flutter 中文开发者网站 - Flutter 1、查看mac电脑处理器选择sdk 2、解压 unzip ~/Downloads/flutter_macos_arm64_3.32.2-stable.zip \ -d ~/development/ 3、添加环境变量 命令行打开配置环境变量文件 ope…...

GeoServer发布PostgreSQL图层后WFS查询无主键字段

在使用 GeoServer&#xff08;版本 2.22.2&#xff09; 发布 PostgreSQL&#xff08;PostGIS&#xff09;中的表为地图服务时&#xff0c;常常会遇到一个小问题&#xff1a; WFS 查询中&#xff0c;主键字段&#xff08;如 id&#xff09;莫名其妙地消失了&#xff01; 即使你在…...