【数据结构与算法】4、双向链表(学习 jdk 的 LinkedList 部分源码)

news/2024/11/8 2:56:30/

目录

  • 一、双向链表
  • 二、node(int index) 根据索引找节点
  • 三、clear()
  • 四、add(int, E)
  • 五、remove(int index)
  • 六、双向链表和单链表
  • 七、双向链表和动态数组
  • 八、jdk 官方的 LinkedList 的 clear() 方法

一、双向链表

在这里插入图片描述

🎁 单链表的节点中只有一个 next 指针引用着下一个节点的地址
🎁 当要获取单链表中的最后一个元素的时候,需要从头节点开始遍历到最后
🎁 单链表一开始的时候有 first 头指针引用着头节点的地址

💰 双向链表可以提升链表的综合性能
💰 双向链表的节点中有 prev 指针引用着一个节点的地址,有 next 指针引用着一个节点的地址
💰 双向链表中一开始的时候有 first 头指针引用着头节点的地址,有 last 尾指针引用着尾节点的地址
💰 ① 当需要获取双向链表靠后的节点【index >= (size / 2)】的时候从尾节点开始遍历;② 当需要获取双向链表靠前的节点【index < (size / 2)】的时候从头节点开始遍历

🎄 头节点的 prev 为 null
🎄 尾节点的 next 为 null

二、node(int index) 根据索引找节点

  /*** 根据索引找节点*/private Node<E> node(int index) {rangeCheck(index);if (index < (index >> 1)) { // 找左边的节点Node<E> node = first;for (int i = 0; i < index; i++) {node = node.next;}return node;} else {Node<E> node = last;for (int i = size - 1; i > index; i--) {node = node.prev;}return node;}}

三、clear()

  @Overridepublic void clear() {size = 0;first = null;last = null;}

只有被【gc root 对象】引用的对象才不会被垃圾回收器回收:

🍃 被栈指针(局部变量)引用的对象是 gc root 对象

在这里插入图片描述

在这里插入图片描述

四、add(int, E)

在这里插入图片描述

🎄 ① 当往索引 0 处插入节点的时候
🎄 ② 当往最后插入节点的时候
🎄 ③ 当一个节点都没有(插入第一个节点)的时候

在这里插入图片描述

在这里插入图片描述

    @Overridepublic void add(int index, E element) {rangeCheck4Add(index);if (size == 0 || (first == null && last == null)) { // 添加第一个节点// 新节点的 prev 和 next 都指向 nullfirst = new Node<>(element, null, null);// 头指针和尾指针都指向新节点last = first;} else {if (index == size) { // 往最后插入新节点Node<E> oldLast = last;last = new Node<>(element, oldLast, null);oldLast.next = last;} else {// 找到 index 索引处的节点(该节点是新节点的下一个节点)Node<E> next = node(index);// 前一个节点(假如是往索引为 0 的位置插入节点的话, prev 是 null)Node<E> prev = next.prev;// 新节点的 prev 指向【前一个节点】// 新节点的 next 指向【后一个节点】Node<E> newNode = new Node<>(element, prev, next);// 后一个节点的 prev 指向新节点next.prev = newNode;/* 往索引为 0 处插入新节点 */if (prev == null) { // 往索引为 0 的位置插入新节点(插入新节点到头节点的位置)first = newNode; // 头指针指向新节点} else {// 前一个节点的 next 指向新节点prev.next = newNode;}}}size++;}

在这里插入图片描述

五、remove(int index)

在这里插入图片描述

自己写的代码(测试成功的):

    @Overridepublic E remove(int index) {rangeCheck(index);// 拿到 index 位置的节点Node<E> oldNode = node(index);if (index == 0) { // 删除头节点// 拿到头节点first = oldNode.next;first.prev = null;} else {oldNode.prev.next = oldNode.next;if (oldNode.next == null) { // 删除尾节点last = oldNode.prev;} else {oldNode.next.prev = oldNode.prev;}}size--;return oldNode.element;}

老师的代码:

    @Overridepublic E remove(int index) {rangeCheck(index);Node<E> node = node(index);Node<E> prev = node.prev;Node<E> next = node.next;if (prev == null) { // 删除头节点first = next;} else {prev.next = next;}if (next == null) { // 删除尾节点last = prev;} else {next.prev = prev;}size--;return node.element;}

在这里插入图片描述

六、双向链表和单链表

在这里插入图片描述

🎉 双向链表相比单向链表操作数量缩减一半

七、双向链表和动态数组

🌱 动态数组:开辟、销毁内存空间的次数相对较少但可能造成内存空间浪费(可以通过缩容解决)
🌱 双向链表:开辟、销毁内存空间的次数相对较多(每次添加元素都会创建新的内存空间 ),但不会造成内存空间的浪费

🌿 如果需要频繁在尾部进行添加删除操作,动态数组双向链表 均可选择

  • 动态数组在尾部添加和删除都是 O(1) 级别的
  • 双向链表由于有尾指针 last 的存在,在尾部添加和删除的操作也是 O(1) 级别的

🌿 如果需要频繁在头部进行添加删除操作,建议选择使用 双向链表

  • 动态数组头部的添加和删除操作需要进行大量的元素挪动
  • 双向链表有头指针 first 的存在,在头部进行添加删除操作效率很高

🌿 如果需要频繁地(在任意位置)进行添加删除操作,建议选择使用双向链表

  • 动态数组有最坏情况(假如是在头部添加或删除几乎需要挪动全部元素)
  • 双向链表最多就遍历 n/2 次(n 是元素数量)

🌿 如果有频繁的查询操作(随机访问操作),建议选择使用动态数组

  • 动态数组的随机访问是 O(1) 级别的,通过下标 index 可以直接定位到元素的内存地址
  • 双向链表每次查询都需要遍历,最坏情况要遍历 size 次(size 是元素个数)

❓ 相比单链表,双向链表效率很高。哪有了双向链表,单向链表是否就没有任何用处了呢 ❓
🌿 哈希表的设计中就用到了单链表 😀

八、jdk 官方的 LinkedList 的 clear() 方法

在这里插入图片描述

  • 我学习数据结构与算法的全部代码:https://gitee.com/zgq666good/datastructureandalgorithm.git
  • 学习资料来自于我偶像 ~ 李明杰(小码哥教育)
    在这里插入图片描述

🌿如有错误,请不吝赐教🌿


http://www.ppmy.cn/news/601445.html

相关文章

4141:砝码称重

#include<iostream> using namespace std; int total0; int dp[1000]; int num[6],sum; int kg[6]{1,2,3,5,10,20}; int main(void){ for(int i0;i<6;i){ cin>>num[i]; sumnum[i]*kg[i]; } dp[0]1; //对于第i种砝码&#xff0c;枚…

java8函数式接口使用详解

在Java8之前&#xff0c;我们通常使用匿名内部类来实现接口的抽象方法&#xff0c;例如&#xff1a; //定义一个接口 interface Greeting {void sayHello(String name); }//使用匿名内部类实现接口 Greeting greeting new Greeting() {Overridepublic void sayHello(String n…

pcie转m2装系统win10_NVMe SSD安装Win10系统详解:小白秒懂

最近有不少小伙伴问我&#xff0c;他们自己买了个PCIe SSD&#xff0c;不知道怎么装系统。如果一个人问还好&#xff0c;但是如果很多人问同样的问题&#xff0c;那某冬索性写个PCIe SSD装系统的教程给大家看好了。 要装系统进PCIe SSD&#xff0c;当然我们得有个PCIe SSD。 这…

硬件篇-配置

写在最前 这已经可以成为垃圾佬配置了。。。 机箱->239元 机箱选用的itx迷你机箱&#xff0c;为了后期nas方便拓展选了4盘位&#xff0c;该机箱还是比较符合我的预期的&#xff0c;颇有种麻雀虽小五脏俱全的感觉&#xff0c;机箱可以安装matx主板和itx主板&#xff0c;还是…

租房小程序源码推荐,让你的租房平台更有竞争力

为租房平台的从业者&#xff0c;你是否也曾为如何提高平台的竞争力而苦恼&#xff1f;租房小程序源码或许是一个不错的选择。 租房小程序源码是一种可以让你快速搭建一个专属的租房平台的工具&#xff0c;可以帮助你快速上线一个符合市场需求的租房平台。相较于从头开始开发一…

nginx超时相关参数

#读取http body的超时时间&#xff0c;单位秒&#xff0c;连接建立后&#xff0c;服务端接收body&#xff0c;规定时间内没收到&#xff0c;则超时&#xff0c;返回给客服端408&#xff08;request time out&#xff09; client_body_timeout 1000; #发送响应超时时间&…

第六章、Linux文件与目录管理

6.1 目录与路径 6.1.1 相对路径与绝对路径 绝对路径&#xff1a;路径的写法“一定由根目录 / 写起”&#xff0c;例如&#xff1a; /usr/share/doc 这个目录。 相对路径&#xff1a;路径的写法“不是由 / 写起”&#xff0c;例如由 /usr/share/doc 要到 /usr/share/man 下面…

修改注册表关闭 Vari-Bright,无需安装 Radeon 显卡控制台,禁用“屏幕自适应亮度调节”

情景描述&#xff1a;它会在屏幕颜色浅的时候调亮屏幕&#xff0c;在颜色深的时候调暗屏幕&#xff0c;这样看电影什么的碰到暗的画面它还调暗&#xff0c;结果整个屏幕就变成了一坨浆糊&#xff0c;亮的时候又亮的刺眼。这到底是什么逻辑啊。。。&#xff1f;而且这个功能不受…