[Data structure]双链表 | 一文带你了解线性数据结构之一的双链表

news2025/1/21 18:52:35

⭐作者介绍:大二本科网络工程专业在读,持续学习Java,努力输出优质文章
⭐作者主页:@逐梦苍穹
⭐所属专栏:数据结构

双链表

  • 1、简介
  • 2、常见操作
  • 3、时间复杂度
  • 4、代码实现思路总览
  • 5、Node
  • 6、DoubleLinkedList
    • 6.1、添加节点
      • 6.1.1、addNode
      • 6.1.2、addNodeByOrder
    • 6.2、删除节点
    • 6.3、修改节点
    • 6.4、显示链表内容

1、简介

  双向链表(Doubly linked list)是一种常见的线性数据结构,它与单链表类似,但每个节点不仅包含一个指向下一个节点的指针,还包含一个指向前一个节点的指针。
  这使得双向链表可以在需要正向和反向遍历的场景中提高效率。

2、常见操作

下面是双向链表的一些基本操作:

  1. 链表的创建:与单链表类似,在创建链表时需要创建头节点,并将其前驱指针和后继指针都设置为NULL。然后逐个创建后续节点,将前一个节点的后继指针指向当前节点,将当前节点的前驱指针指向前一个节点,直到创建完整个链表。
  2. 链表的遍历:与单链表类似,从头节点开始,逐个遍历链表中的节点,直到遍历到尾节点为止。
  3. 链表的插入:可以在链表的任意位置插入一个新节点。具体操作是先将新节点的指针指向其下一个节点,然后将前一个节点的后继指针指向新节点,将下一个节点的前驱指针指向新节点。
  4. 链表的删除:可以删除链表中的任意节点。具体操作是将待删除节点的前一个节点的后继指针指向待删除节点的下一个节点,将下一个节点的前驱指针指向待删除节点的前一个节点,然后释放待删除节点的内存空间。

3、时间复杂度

双向链表的时间复杂度如下:

  1. 链表的创建时间复杂度为O(n),其中n为链表中节点的个数。
  2. 链表的遍历时间复杂度为O(n),其中n为链表中节点的个数。
  3. 链表的插入时间复杂度为O(1)。
  4. 链表的删除时间复杂度为O(1)。

需要注意的是,与单链表相比,双向链表需要占用更多的内存空间,因为每个节点需要同时保存前驱指针和后继指针。此外,双向链表在需要正向和反向遍历的场景中表现更好,但在只需要正向遍历的场景中,其效率与单链表相当。

4、代码实现思路总览

双链表与单链表在实现过程中最大的不同是:在实现的方法中,不再需要遍历到 目标节点的前一个节点,而是直接遍历到目标节点。
回顾一下单链表:遍历到目标节点的前一个节点的原因:单链表是单向的,如果直接遍历到目标节点,则无法修改目标节点前一个节点的指针指向,会破坏链表结构。
而双链表则没有这个问题,因为多了一个前驱指针pre

实现过程简要分析如下:
在这里插入图片描述

  首先创建节点对象代码Node类,创建成员变量:唯一标识符id,节点的值value,节点Node类型的next、节点Node类型的pre(重点)以及重写toString方法。
  然后创建双链表类DoubleLinkedList,先初始化头节点,然后依次编写各种方法:获取头节点、添加节点(两种方式)、删除节点、修改节点、显示链表内容。[总体实现思路如下图所示]:
在这里插入图片描述

5、Node

重写的toString方法,next和pre不直接打印对象内容,而是打印十六进制的地址值:
在这里插入图片描述

这里还运用了三元表达式.

下面是Node的代码:

package linkedList.doubleLinkedList;

/**
 * @author 逐梦苍穹
 * @date 2023/5/12 10:00
 */

public class Node {
    public int id;
    private int value;
    private Node next;
    private Node pre;

    public Node(int id, int value) {
        this.id = id;
        this.value = value;
    }

    @Override
    public String toString() {
        return "Node{" +
                "id=" + id +
                ", value=" + value +
                ", next=" + (next == null ? null
                : ("0x" + Integer.toHexString(next.hashCode()))) +
                ", pre=0x" + Integer.toHexString(pre.hashCode()) +
                '}';
    }

    public int getValue() {
        return value;
    }

    public void setValue(int value) {
        this.value = value;
    }

    public Node getNext() {
        return next;
    }

    public void setNext(Node next) {
        this.next = next;
    }

    public Node getPre() {
        return pre;
    }

    public void setPre(Node pre) {
        this.pre = pre;
    }
}

6、DoubleLinkedList

双链表的全部代码如下,后面对其详细拆分解析:

package linkedList.doubleLinkedList;

/**
 * @author 逐梦苍穹
 * @date 2023/5/12 10:00
 */
public class DoubleLinkedList {
    private final Node headNode = new Node(0, 0);

    /**
     * 获取头节点
     */
    public Node getHeadNode() {
        return headNode;
    }

    /**
     * 添加到尾部
     */
    public void addNode(Node node) {
        Node temp = this.headNode;
        while (true) {
            if (temp.getNext() == null) {
                break;
            } else {
                //辅助节点后移
                temp = temp.getNext();
            }
        }
        //跳出循环说明到达链表尾部,可以添加
        temp.setNext(node); //把最后一个节点的next指针置为下一个要添加的节点的地址值
        node.setPre(temp); //把添加到末尾的节点的pre前置指针置为上一个节点的地址值
    }

    /**
     * 根据id查找位置插入
     */
    public void addNodeByOrder(Node node) {
        Node temp = this.headNode;

        while (true) {
            if (temp.getNext() == null) { // 如果temp是尾节点
                temp.setNext(node);
                node.setPre(temp); // 设置新节点的前驱节点为temp
                break;
            }
            if (node.id < temp.getNext().id) { // 如果新节点应该插入到temp之后
                node.setNext(temp.getNext());
                node.setPre(temp); // 设置新节点的前驱节点为temp
                temp.getNext().setPre(node); // 设置新节点之后的节点的前驱节点为node
                temp.setNext(node);
                break;
            }
            temp = temp.getNext();
        }
    }

    /**
     * 删除节点
     */
    public void deleteNode(int id) {
        Node temp = this.headNode;
        if (temp.getNext() == null) {
            System.out.println("链表空无法删除");
            return;
        }
        while (true) {
            if (temp.getNext() == null) break;
            if (temp.id == id) {
                temp.getPre().setNext(temp.getNext());
                temp.getNext().setPre(temp.getPre());
                System.out.println("Node{id=" + id + "}:删除成功");
                return;
            }
            temp = temp.getNext();
        }
        System.out.println("无此节点");
    }

    /**
     * 修改节点
     */
    public void updateNode(int id, int newValue) {
        Node temp = this.headNode;
        if (temp.getNext() == null) {
            System.out.println("链表空");
        }
        while (true) {
            if (temp.getNext() == null) break;
            if (temp.id == id) {
                temp.setValue(newValue);
                System.out.println("Node{id=" + id + ", newValue=" + newValue + "}");
                return;
            }
            temp = temp.getNext();
        }
        System.out.println("无此节点");
    }

    /**
     * 打印整个双链表的内容
     */
    public void outDoubleLinkedList() {
        Node temp = this.headNode.getNext();
        if (temp == null) {
            System.out.println("链表空");
            return;
        }
        while (true) {
            System.out.println(temp);
            if (temp.getNext() == null) break;
            temp = temp.getNext();
        }
    }
}

6.1、添加节点

添加节点有两种方式:直接添加到链表尾部、按唯一标识id按顺序添加

6.1.1、addNode

在这里插入图片描述

直接添加到链表尾部:先判断当前节点的next域是不是null,如果是,说明到达链表尾部,可以添加;如果不是,则链表指针需要后移,直到抵达尾部:
在这里插入图片描述

6.1.2、addNodeByOrder

在这里插入图片描述

按唯一标识id按顺序添加:
  这里在while中有两个if判断,一开始是指向头节点的,这时候链表如果为空,则直接添加数据即可;
  当链表不为空的时候,进入的是第二个if分支,由于每一次插入的时候,都是保持有序的,所以在插入节点的时候只需要判断:想要插入节点的位置的下一个节点的id大于想要插入节点的id即可。
在这里插入图片描述

6.2、删除节点

在这里插入图片描述

  删除节点的操作:找到待删除节点,把 该节点的pre的next指针 指向 该节点的next,然后把 该节点的next指针的pre 指向 该节点的pre
  被删除的节点,是不需要手动释放资源的,JVM有自动回收机制。
在这里插入图片描述

6.3、修改节点

在这里插入图片描述
在这里插入图片描述

6.4、显示链表内容

  显示链表内容的思路,其实无非也就是遍历。这里采用的是while进行遍历,因为并不知道链表到底有多长,所以采用while遍历更合适。
  遍历终止条件:链表为空 或者 到达最后一个节点(即在判断不为空的情况下出现next为null)
在这里插入图片描述

写在最后
  ⭐如果觉得文章写的不错,欢迎点个关注一键三连。您的三连支持,是我创作的最大动力
  ⭐有写的不好的地方也欢迎指正,一同进步😁

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

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

相关文章

设计模式——对象创建模式之工厂模式

文章目录 前言一、“单一职责” 模式二、Factory Method 工厂方法1、动机2、模式定义3、伪代码示例4、结构 总结 前言 一、“单一职责” 模式 通过“对象创建”模式绕开new&#xff0c;来避免对象创建&#xff08;new&#xff09;过程中所导致的紧耦合&#xff08;依赖具体类&…

Linux系统之dstat命令的基本使用

Linux系统之dstat命令的基本使用 一、dstat命令介绍1. dstat简介2. dstat特点 二、本次实践介绍1. 本地环境规划2. 本次实践介绍 三、本地环境检查1. 检查操作系统版本2. 查看系统内核版本3. 检查本地yum仓库源状态 三、安装dstat工具1. 搜索dstat软件2. 安装dstat工具3. 查看d…

海康威视 2024届 数字逻辑设计 实习笔试分析

说明 记录一下 5月11日晚&#xff0c;做的海康威视的一场笔试。分享给需要的IC人。 岗位&#xff1a;数字逻辑设计工程师&#xff08;浙江 杭州&#xff09; 转载需要本人同意&#xff01; 我的见解不一定都是准确的&#xff0c;欢迎评论区交流指正~~ 单选题 1、&#xff…

springboot+vue漫画之家系统(源码+文档)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的漫画之家系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 &#x1f495;&#x1f495;作者&#xff1a;风歌&a…

TweenMax介绍

GSAP 之 TweenMax 介绍&#xff08;一&#xff09; 一、背景 GreenSock &#xff08;绿袜子&#xff09; GreenSock 是一家做 专业级 JavaScript 动画的公司&#xff0c;主要产品就是其下的 GSAP (GreenSock Animation Platform)&#xff0c;配合着 GSPA 开发了很多专业的动画…

做一个好玩的,给小猫拍照。web 端实现,发布图片,浏览图片。

0&#xff1a;先试试看 hongweizhu.com/#/cat 。 1&#xff1a;上班的路上会路过一家宠物店&#xff0c;里面有一只小猫&#xff0c;给它拍点照片&#xff0c;增加一点乐趣。 2: 使用到的技术 MongoDB 数据库&#xff08;我暂时不想把图片直接放到服务器某个目录上&#xff0c;…

你对这4个ICT行业的网络设备,可能一无所知

晚上好&#xff0c;我是老杨。 上个月给你整了篇安全方向的报告分析&#xff0c;反响不错。 那篇主要是对网络安全的就业前景和怎么入门进行了具体分析&#xff0c;没看过的可以看看&#xff1a;《一不留神&#xff0c;网络安全工程师的岗位需求&#xff0c;破237万了》。 不…

混合精度是如何加速大模型训练的?

混合精度是如何加速大模型训练的&#xff1f; 基础知识回顾float-32从float-32 到float-16 混合精度计算bfloat16 基础知识回顾 float-32 在深度学习中&#xff0c;通常使用float-32 精度的数值训练模型&#xff0c;其中pytorch默认的也是float-32。 float32&#xff0c;也就…

每日一练 | 华为认证真题练习Day43

1、关于访间控制列表编号与类型的对应关系&#xff0c;下面描述正确的是&#xff08;&#xff09;。 A. 基本的访问控制列表编号范围是1000-2999 B. 二层的访问控制列表编号范围是4000-4999 C. 高级的访间控制列表编号范围是3000-4000 D. 基于接口的访问控制列表编号范围是…

IO + File 详细基础知识

文章目录 IO File一、 File二、IO流2.0 IO流介绍2.1 字节流2.1.1 字节输出流 - FileOutputStream2.1.1.1 write方法2.1.1.2 字节输出流细节2.1.1.3代码实现2.1.1.4 换行与续写 2.1.2 字节输入流 - FileInputStream2.1.2.1 read()方法2.1.2.2 字节输入流细节2.1.2.3 代码实现 2…

PostgreSQL-分布式事务之两阶段提交

什么是ACID 在日常操作中&#xff0c;对于一组相关操作&#xff0c;通常需要其全部成功或全部失败。 在关系型数据库中&#xff0c;将这组相关操作称为“事务”。 在一个事务中&#xff0c;多个插入、修改、删除操作要么全部成功&#xff0c;要么全部失败&#xff0c;这称为…

SpringCloud Nacos 注册配置中心

前言 在微服务架构中&#xff0c;注册中心是核心的基础服务之一。相信不少同学都用过 Dubbo 这个流行分布式框架&#xff0c;很久之前微服务还没这么盛行&#xff0c;Dubbo就提供了比较完善的服务治理功能&#xff0c;而服务治理的实现主要依靠的就是注册中心。 许多同学接触…

Apache Kafka - 生产者内存优化注意事项

文章目录 1. 调优内存池参数2. 限制客户端生产速率3. 减小单条消息大小4. 监控生产者内存和性能5. 评估topic的partition分布6. 增加更多生产者实例7. Kafka升级和更强劲的硬件小结 1. 调优内存池参数 增大batchSize和linger ms,适当延长消息在内存池的最大延迟,减少发送次数。…

【C++初阶】第十二篇:priority_queue的使用与模拟实现

文章目录 priority_queue的使用priority_queue的介绍priority_queue的定义方式priority_queue各个接口的使用 仿函数代码样例使用场景&#xff08;示例&#xff09; priority_queue的模拟实现堆的向上调整算法堆的向下调整算法priority_queue的模拟实现 总结 priority_queue的使…

redis:基于 Streams 的消息队列

前言 Redis 5.0 及 5.0 以后的版本提供的Streams 是专门为消息队列设计的数据类型&#xff0c;它提供了丰富的消息队列操作命令。 消息队列 Streams 操作 XADD&#xff1a;插入消息&#xff0c;保证有序&#xff0c;可以自动生成全局唯一 ID&#xff1b; 名称为 mqstream 的…

【第七章:输入输出系统】

目录 知识框架No.0 引言No.1 输入输出系统一、基本概念二、I/O控制方式1、主机如何与I/O设备进行交互?2、CPU是如何通过I/O接口与外设交互的3、如何判断读入的数据有没有被输入完成呢&#xff1f;4、对于快速I/o设备&#xff0c;如“磁盘”&#xff0c;每准备好一个字就给CPu发…

【C语言】操作符详解(下)

操作符详解 1.条件操作符2. 逗号表达式3.下标引用&#xff0c;函数调用和结构体成员4.表达式求值4.1隐式类型转换4.2算术转换 5.操作符的属性 所属专栏&#xff1a;C语言 博主首页&#xff1a;初阳785 代码托管&#xff1a;chuyang785 感谢大家的支持&#xff0c;您的点赞和关注…

原生js手动实现一个多级树状菜单效果(高度可过渡变化) + 模拟el-menu组件实现(简单版)

文章目录 学习链接效果图代码要点 简单模拟el-menu实现TestTree.vueMenu.vueSubMenu.vue 学习链接 vue实现折叠展开收缩动画 - 自己的链接 elment-ui/plus不定高度容器收缩折叠动画组件 - 自己的链接 Vue transition 折叠类动画自动获取隐藏层高度以及手风琴效果实现 vue t…

Sqoop: Hadoop数据传输的利器【Sqoop实战】【上进小菜猪大数据系列】

我是上进小菜猪&#xff0c;沈工大软件工程专业&#xff0c;爱好敲代码&#xff0c;持续输出干货&#xff0c;欢迎关注。 Sqoop: Hadoop数据传输的利器, 在大数据领域&#xff0c;数据的传输和集成是至关重要的任务之一。Sqoop&#xff08;SQL to Hadoop&#xff09;作为Apache…

ChatGPT的前世今生,到如今AI领域的竞争格局,本文带你一路回看!

73年前&#xff0c;“机器思维”的概念第一次被计算机科学之父艾伦图灵&#xff08;Alan Turing&#xff09;提出&#xff0c;从此&#xff0c;通过图灵测试成为了人类在AI领域为之奋斗的里程碑目标。 73年后的今天&#xff0c;在AI历经了数十年的不断进化、迭代后&#xff0c…