Java实现数据结构——双链表

目录

一、前言

二、实现

2.1 类的创建

三、对链表操作实现

3.1 打印链表

3.2 插入数据

3.2.1 申请新节点

3.2.2 头插

​编辑

3.2.3 尾插

3.2.4 链表长度

3.2.5 任意位置插入

3.3 删除数据

3.3.1 头删

3.3.2 尾删

3.3.3 删除指定位置数据

3.3.4 删除指定数据

3.3.5 删除所有指定数据 

3.3.6 删除链表 

四、LinkedList

4.1 什么是 LinkedList

4.2 LinkedList 的使用

​编辑

4.2.1 LinkedList的构造方法

4.2.2 常用方法

​编辑

​编辑

五、ArrayList和LinkedList的区别


一、前言

更详细的理论请移步笔者的另一文章

http://t.csdnimg.cn/4Mtne

二、实现

2.1 类的创建

双向链表就是在单链表的基础上加上了一个 prev,存放上一个节点的地址

public class MyLinkedList {//    自己实现双向链表static class ListNode {int val;ListNode prev;//前ListNode next;//后public ListNode(int val) {this.val = val;}}ListNode head = null;//头ListNode last = null;//尾
}

需要什么方法在后续再补充

三、对链表操作实现

3.1 打印链表

可正序打印

也可逆序打印

 public void printHead() {//正序打印ListNode cur = this.head;if (cur == null) {System.out.println("当前链表为空!");return;}while (cur != null) {System.out.println(cur.val);cur = cur.next;}}public void printLast(){//逆序打印ListNode cur = this.last;if(cur == null){System.out.println("当前链表为空!");return;}while (cur != null) {System.out.println(cur.val);cur = cur.prev;}}

3.2 插入数据

3.2.1 申请新节点

   public ListNode buyNode(int data) {
//    申请新节点ListNode newnode = new ListNode(data);return newnode;}

3.2.2 头插

链表为空就让链表的 headlast 都等于这个新节点

若链表不为空

原头节点的 prev 保存新插入节点的地址

新插入节点的 next 保存原头节点的地址

新插入节点成为新的头节点

 public void addFirst(int data) {//头插if (this.head == null) {this.head = buyNode(data);this.last = this.head;return;}ListNode newnode = buyNode(data);newnode.next = this.head;//新插入节点的 next 保存原头节点的地址this.head.prev = newnode;//原头节点的 prev 保存新插入节点的地址this.head = newnode;//新插入节点成为新的头节点}

测试

 public static void main(String[] args) {MyLinkedList linkedList = new MyLinkedList();linkedList.addFirst(1);linkedList.addFirst(2);linkedList.addFirst(3);linkedList.addFirst(4);linkedList.printHead();System.out.println("==============");linkedList.printLast();}


3.2.3 尾插

链表为空就让链表的 headlast 都等于这个新节点

若链表不为空

last.next 保存新插入节点的地址

新插入节点的 prev 保存 last 的地址

新插入节点成为 last 

public static void main(String[] args) {MyLinkedList linkedList = new MyLinkedList();linkedList.addLast(5);linkedList.addLast(6);linkedList.addLast(7);linkedList.addLast(8);linkedList.printHead();System.out.println("==============");linkedList.printLast();}

测试

public static void main(String[] args) {MyLinkedList linkedList = new MyLinkedList();linkedList.addLast(5);linkedList.addLast(6);linkedList.addLast(7);linkedList.addLast(8);linkedList.printHead();System.out.println("==============");linkedList.printLast();}


3.2.4 链表长度

 public int size() {//返回链表节点个数ListNode cur = this.head;int count = 0;while (cur != null) {count++;cur = cur.next;}return count;}

3.2.5 任意位置插入

插入时需要检查坐标的合法性

合法区间是[0,size()]

指定位置合法后

新节点的 prev 存储原位置节点 prev 的地址

新节点的 next 存储原位置节点的地址

原位置的 prev 存储为新节点的地址

原位置前一节点的 next 存储为新节点的地址

为方便观察修改一下打印方法

  public void printHead() {//正序打印ListNode cur = this.head;if (cur == null) {System.out.println("当前链表为空!");return;}while (cur != null) {System.out.print(cur.val + " ");cur = cur.next;}}public void printLast() {//逆序打印ListNode cur = this.last;if (cur == null) {System.out.println("当前链表为空!");return;}while (cur != null) {System.out.print(cur.val + " ");cur = cur.prev;}}

测试

 public static void main(String[] args) {MyLinkedList linkedList = new MyLinkedList();linkedList.addLast(5);linkedList.addLast(6);linkedList.addLast(7);linkedList.addLast(8);linkedList.printHead();System.out.println();System.out.println("==========");linkedList.addAny(1, 99);linkedList.addAny(2, 199);linkedList.addAny(3, 299);linkedList.addAny(0, 122);linkedList.addAny(linkedList.size(), 999);linkedList.printHead();}

 


3.3 删除数据

3.3.1 头删

由于此处是基础数据类型

不需要对节点中存储的数据进行置空

如果存储的是引用数据类型就需要置空

将原头节点置空

头节点的下一个节点成为新的头节点

新的头节点的 prev 需要置空

  public void removeFirst() {//头删if (this.head == null) {System.out.println("当前链表为空!");return;}ListNode tmp = this.head.next;
//        this.head.val = null;//由于此处是基础数据类型 不需要对节点中存储的数据进行置空 如果存储的是引用数据类型就需要置空this.head = null;this.head = tmp;if(this.head == null){this.last = null;}else {this.head.prev = null;//新的头节点的 prev 需要置空}}

测试

  public static void main(String[] args) {MyLinkedList linkedList = new MyLinkedList();linkedList.addFirst(1);linkedList.addFirst(2);linkedList.addFirst(3);linkedList.addFirst(4);linkedList.printHead();linkedList.removeFirst();System.out.println();linkedList.printHead();linkedList.removeFirst();System.out.println();linkedList.printHead();linkedList.removeFirst();System.out.println();linkedList.printHead();linkedList.removeFirst();System.out.println();linkedList.printHead();}


3.3.2 尾删

由于此处是基础数据类型

不需要对节点中存储的数据进行置空

如果存储的是引用数据类型就需要置空

尾节点的前一个节点成为新的尾节点

新的尾节点的 next 需要置空

   public void removeLast() {//尾删if (this.last == null) {System.out.println("当前链表为空!");return;}
//        this.last.val = null;由于此处是基础数据类型 不需要对节点中存储的数据进行置空 如果存储的是引用数据类型就需要置空ListNode tmp = this.last.prev;this.last = null;this.last = tmp;if(this.last == null){this.head = null;}else {this.last.next = null;//新的尾节点的 next 需要置空}}

测试

    public static void main(String[] args) {MyLinkedList linkedList = new MyLinkedList();linkedList.addFirst(1);linkedList.addFirst(2);linkedList.addFirst(3);linkedList.addFirst(4);linkedList.printHead();linkedList.removeLast();System.out.println();linkedList.printHead();linkedList.removeLast();System.out.println();linkedList.printHead();linkedList.removeLast();System.out.println();linkedList.printHead();linkedList.removeLast();System.out.println();linkedList.printHead();}


3.3.3 删除指定位置数据

删除时需要检查坐标的合法性

合法区间是[0,size())

将删除位置前节点的 next 保存为删除节点位置后节点的地址

将删除位置后节点的 prev 保存为删除节点位置前节点的地址

  public void removeAny(int index) {if (this.head == null) {System.out.println("当前链表为空!");return;}if (index < 0 && index >= this.size()) {throw new IndexillegalityException("下标不合法!");}if (index == 0) {removeFirst();return;} else if (index == size() - 1) {removeLast();return;}ListNode cur = this.head;while (index != 0) {//找要删除的节点cur = cur.next;index--;}cur.prev.next = cur.next;cur.next.prev = cur.prev;cur = null;}

测试

 public static void main(String[] args) {MyLinkedList linkedList = new MyLinkedList();linkedList.addFirst(1);linkedList.addFirst(2);linkedList.addFirst(3);linkedList.addFirst(4);linkedList.printHead();System.out.println();linkedList.removeAny(1);linkedList.printHead();System.out.println();linkedList.removeAny(1);linkedList.printHead();System.out.println();linkedList.removeAny(1);linkedList.printHead();System.out.println();linkedList.removeAny(1);linkedList.printHead();System.out.println();}

抛了一个空指针异常

说明在链表只剩下一个节点的时候需要特殊处理

    public void removeAny(int index) {if (this.head == null) {System.out.println("当前链表为空!");return;}if (index < 0 && index >= this.size()) {throw new IndexillegalityException("下标不合法!");}if (index == 0) {removeFirst();return;} else if (index == size() - 1) {removeLast();return;}ListNode cur = this.head;while (index != 0) {//找要删除的节点cur = cur.next;index--;}if(cur == null ){//判断是否只有一个节点//cur在移动后如果等于空,就说明一定只剩下一个节点this.last = null;this.head = null;}else {cur.prev.next = cur.next;cur.next.prev = cur.prev;cur = null;}}


3.3.4 删除指定数据

将删除位置前节点的 next 保存为删除节点位置后节点的地址

将删除位置后节点的 prev 保存为删除节点位置前节点的地址

对头和尾做单独处理

 public void removeData(int data){//删除指定数据if (this.head == null) {System.out.println("当前链表为空!");return;}ListNode cur = this.head;while (cur != null){if(cur.val == data){if(cur == head){removeFirst();return;}else if(cur == last){removeLast();return;}else {cur.prev.next = cur.next;cur.next.prev = cur.prev;}}cur = cur.next;}System.out.println("当前链表中没有您要删除的数据");}

测试

 public static void main(String[] args) {MyLinkedList linkedList = new MyLinkedList();linkedList.addFirst(1);linkedList.addFirst(2);linkedList.addFirst(3);linkedList.addFirst(4);linkedList.printHead();System.out.println();linkedList.removeData(4);linkedList.printHead();System.out.println();linkedList.removeData(3);linkedList.printHead();System.out.println();linkedList.removeData(2);linkedList.printHead();System.out.println();linkedList.removeData(1);linkedList.printHead();}


3.3.5 删除所有指定数据 

将链表中所有节点值等于 data 的节点全部删除

将3.3.4 的方法中进行删除后继续遍历链表

遇到节点值为 data 的继续删除即可

  public void removeDataAll(int data){//删除所有节点值为data的节点if (this.head == null) {System.out.println("当前链表为空!");return;}boolean flg = false;ListNode cur = this.head;while (cur != null){if(cur.val == data){flg = true;if(cur == head){removeFirst();}else if(cur == last){removeLast();}else {cur.prev.next = cur.next;cur.next.prev = cur.prev;}}cur = cur.next;}if(!flg){System.out.println("当前链表中没有您要删除的数据!");}else {System.out.println("删除成功!");}}

测试

public static void main(String[] args) {MyLinkedList linkedList = new MyLinkedList();linkedList.addFirst(4);linkedList.addFirst(4);linkedList.addFirst(4);linkedList.addFirst(4);linkedList.addFirst(2);linkedList.addFirst(1);linkedList.addFirst(5);linkedList.addFirst(6);linkedList.addFirst(4);linkedList.printHead();System.out.println();linkedList.removeDataAll(4);linkedList.printHead();System.out.println();}


3.3.6 删除链表 

即将所有节点全部删除

让每个节点的 next ,prev 都置空

  public void clear(){//删除整个链表if (this.head == null) {System.out.println("当前链表为空!");return;}ListNode cur = this.head;while (cur!= null){
//            cur.val = null;如果是引用数据类型就需要置空ListNode tmp = cur.next;cur.next = null;cur.prev = null;cur = tmp;}this.head = null;this.last = null;}

四、LinkedList

Java 中已经封装好了 LinkedList

4.1 什么是 LinkedList

LinkedList 官方文档 https://docs.oracle.com/javase/8/docs/api/java/util/LinkedList.html

LinkedList  的底层是双向链表结构 ( 链表后面介绍 ) ,由于链表没有将元素存储在连续的空间中,元
素存储在单独的节点中,然后通过引用将节点连接起来了,因此在在任意位置插入或者删除元素
时,不需要搬移元素,效率比较高。
1. LinkedList  实现了  List 接口
2. LinkedList  的底层使用了双向链表
3. LinkedList  没有实现  RandomAccess  接口,因此  LinkedList  不支持随机访问
4. LinkedList  的任意位置插入和删除元素时效率比较高,时间复杂度为 O(1)
5. LinkedList  比较适合任意位置插入的场景

4.2 LinkedList 的使用

4.2.1 LinkedList的构造方法

    public static void main(String[] args) {LinkedList<Integer> linkedList = new LinkedList<>();LinkedList<Number> linkedList2 = new LinkedList<>();}


4.2.2 常用方法

方法功能
boolean add (E e)
尾插 e
void add (int index, E element)
将 e 插入到 index 位置
e 插入到 index 位置
boolean addAll (Collection<? extends E> c)
尾插 c 中的元素
E remove (int index)
删除 index 位置元素
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 的下标
List<E> subList (int fromIndex, int toIndex)
截取部分 list
…………

这里只说说 addAll subList

 public static void main(String[] args) {LinkedList<Integer> linkedList = new LinkedList<>();linkedList.add(new Integer(2));linkedList.add(new Integer(3));linkedList.add(new Integer(4));linkedList.add(new Integer(5));System.out.println(linkedList);List<Integer> list2 = linkedList.subList(1,3);System.out.println(list2);linkedList.set(1,10);System.out.println(list2);}

与 ArrayList 实现的 subList

subList 只是将对应区间的地址截取出来返回

而不是产生新的对象返回


与 ArrayList 实现的 addAll 类似

形参 c 的类型必须是实现了 Collection 接口 

且其中存放的数据必须是 E 本身或 E 的子类

 public static void main(String[] args) {LinkedList<Integer> linkedList = new LinkedList<>();linkedList.add(new Integer(2));linkedList.add(new Integer(3));linkedList.add(new Integer(4));linkedList.add(new Integer(5));LinkedList<Number> linkedList2 = new LinkedList<>(linkedList);System.out.println(linkedList);System.out.println(linkedList2);}

五、ArrayListLinkedList的区别

不同点
ArrayList
LinkedList
存储空间上
物理上一定连续
逻辑上连续,但物理上不一定连续
随机访问
支持 O(1)
不支持: O(N)
头插
需要搬移元素,效率低 O(N)
只需修改引用的指向,时间复杂度为 O(1)
插入
空间不够时需要扩容
没有容量的概念
应用场景
元素高效存储 + 频繁访问
任意位置插入和删除频繁

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

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

相关文章

C++ PImpl模式、指向实现的指针、PImpl Idiom、隐藏实现细节

C PImpl模式、指向实现的指针、PImpl Idiom、隐藏实现细节 flyfish PImpl 全称是 “Pointer to Implementation”&#xff0c;在中文中通常翻译为“指向实现的指针”或者“指向实现”。PImpl 是一种编程技巧&#xff0c;通常用于 C 中&#xff0c;通过这种技术&#xff0c;可…

涉案财物管理系统|八大模块可视化展示

涉案财物管理系统DW-S405系统基于物联网技术规范涉案财物管理流程&#xff0c;确保涉案财物的安全性、完整性和合法性&#xff1b;可以提高办案效率&#xff0c;减少办案成本&#xff0c;实现资源共享。 涉案财物管理系统DW-S405主要分为 8 大模块数据展示。 1、案件信息&…

Linux C | 管道open打开方式

Linux C | 管道open打开方式 1.参考 1. 管道 2.现象 是的&#xff0c;这段代码在调用 open(AUDIOIN_FIFO, O_RDONLY) 时可能会被阻塞。原因是 FIFO&#xff08;命名管道&#xff09;在以只读模式打开时&#xff0c;如果没有其他进程以写模式打开该 FIFO&#xff0c;open 调用将…

防火墙综合实验二

目录 实验要求 IP地址配置 需求七 需求八 需求九 需求十 需求十一 实验要求 接防火墙综合实验一&#xff01; 7&#xff0c;办公区设备可以通过电信链路和移动链路上网(多对多的NAT&#xff0c;并且需要保留一个公网IP不能用来转换)。 8&#xff0c;分公司设备可以通过…

美无定论,娜扎亦菲各自绽放你更爱哪一款?

娜扎亦菲各自绽放你更爱哪一款&#xff1f; 哎呀&#xff0c;这个问题可真是让我头疼呢&#xff0c; 就像让我在两个糖果店里选择最甜的那一颗一样难&#xff01; 古力娜扎和刘亦菲&#xff0c;两位都是娱乐圈里璀璨的明珠&#xff0c; 美得各有千秋&#xff0c;让人怎么舍得…

python爬虫js逆向入门

js基础 在 JavaScript 逆向工程中&#xff0c;常常会遇到一些复杂的代码结构&#xff0c;这些代码可能包含各种函数定义、对象操作、异步调用等。 ### 1. 函数声明和调用function generateSignature(uri, params) { // 将 uri 和参数对象转换为字符串 var data uri ? Objec…

C++基础入门(上)

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 C基础入门(上) 收录于专栏【C语法基础】 本专栏旨在分享学习C的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 1. C发展历史 2. C版本…

如何在SpringCloud项目中实现断路器模式?

在Spring Cloud项目中实现断路器&#xff08;Circuit Breaker&#xff09;模式可以通过几种不同的库实现&#xff0c;如Netflix Hystrix、Resilience4j或Spring Cloud Circuit Breaker。断路器模式允许您的应用在遇到服务故障时快速失败&#xff0c;防止故障的蔓延和系统过载&a…

ffmpeg 找到编解码器,和编解码器上下文后,是如何初始化具体的编解码器的?aac 初始化流程大致如下

1.当使用 avcodec_find_encoder_by_name找到编码器的时候&#xff0c; const AVCodec * aacencoder avcodec_find_encoder_by_name("libx264"); 编码器 有值的是&#xff1a; id AV_CODEC_ID_H264 (27) long_name libx264 H.264…

read paper and make summer (Image enhancement)

read paper and make summer ==标题==:图像==标题==:低级视觉方法==标题==:颜色直方图==标题==:颜色空间变换==标题==:亮度通道==标题==:色度通道==标题==:图像滤波==标题==:方盒滤波==标题==:均值滤波==标题==:高斯滤波(去噪)==标题==:中值滤波(去噪)==标题==:…

Goland 通道

channel通道 目录 channel通道 channel介绍 channel基本使用 有缓存通道和无缓存通道的区别 通道的初始化&#xff0c;写入数据到通道&#xff0c;从通道读取数据及基本的注意事项 channel的关闭和遍历 channel的关闭 为什么关闭 如何优雅地关闭通道 channel的遍历 chan…

小众好玩的赛车游戏:环道巨星 CIRCUIT SUPERSTARS中文安装包

《环道巨星》&#xff08;Circuit Superstars&#xff09;是一款由赛车迷亲手为其他赛车迷打造的俯视角赛车游戏。荟集史上各类赛车运动&#xff0c;旨在提供刺激好玩的驾驶体验&#xff1b;而游戏自带的高技术难度将促使玩家长时间磨砺技巧&#xff0c;以达成完美的一圈。 游戏…

刷题Day50|739. 每日温度、496.下一个更大元素 I、503.下一个更大元素II

开启单调栈篇章&#xff1a;单调栈用来找任意一个元素左边或者右边第一个比它大/小的元素&#xff08;或位置&#xff09;。 739. 每日温度 739. 每日温度 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a;使用一个递增单调栈&#xff0c;可以从头到尾或者从尾到头…

js中scrollIntoView第一次不生效,第二次生效

scrollIntoView 方法用于将元素滚动到可视区域。如果在页面加载后立即使用&#xff0c;可能不会生效&#xff0c;因为页面可能还没有完全渲染或者加载完成。 如果是因为元素还没有完全渲染完成&#xff0c;可以在 requestAnimationFrame 中调用 scrollIntoView。 requestAnima…

C# 建造者模式(Builder Pattern)

建造者模式&#xff08;Builder Pattern&#xff09;&#xff0c;也被称为生成器模式&#xff0c;是一种对象构建模式&#xff0c;旨在将复杂对象的构建过程与表示分离&#xff0c;使得同样的构建过程可以创建不同的表示。这种模式特别适用于构建具有多个组成部分的复杂对象&am…

2024前端面试真题【CSS篇】

盒子模型 盒子模型&#xff1a;box-sizing&#xff0c;描述了文档中的元素如何生成矩形盒子&#xff0c;并通过这些盒子的布局来组织和设计网页。包含content、padding、margin、border四个部分。 分类 W3C盒子模型&#xff08;content-box&#xff09;&#xff1a;标准盒子模…

【系统架构设计师】九、软件工程(面向对象方法|逆向工程)

目录 六、面向对象方法 6.1 基本概念 6.2 面向对象的分析 6.2.1 用例关系 6.2.2 类之间的关系 6.3 面向对象的设计 6.4 面向对象设计原则与设计模式 6.5 面向对象软件的测试 七、逆向工程 历年真题练习 六、面向对象方法 面向对象的分析方法 (Object-Oriented Analys…

python输出/sys/class/power_supply/BAT0/电池各项内容

读取 /sys/class/power_supply/BAT0/ 目录下的所有相关文件,并输出其内容: import os# 定义电池信息文件的路径 battery_path = "/sys/class/power_supply/BAT0/"# 读取文件内容的函数 def read_battery_info(file_name):try:with open(os.path.join(battery_path…

Mybatis-Plus中LambdaQueryWrapper

基本用法 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; // 假设有一个 User 实体类 LambdaQueryWrapper<User> queryWrapper new LambdaQueryWrapper<>(); // 添加查询条件 queryWrapper.eq(User::getName, "John&quo…

stm32出现hardfault-自动化分析map文件

可通过如下python脚本进行分析: import os import sys #在此输入HardFault_Handler时打印的PC指针 address 0x20006980 #以下代码不要要更改 def is_hex(data): try: int(data, 16) return True except ValueError: return False def find_line_number(map_file, addre…