数据结构-链表结构-双向链表

news2024/9/25 1:20:56

双向链表

双向链表的定义

双向链表也叫双链表,与单向链表不同的是,每一个节点有三个区域组成:两个指针域,一个数据域

  • 前一个指针域:存储前驱节点的内存地址
  • 后一个指针域:存储后继节点的内存地址
  • 数据域:存储节点数据

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KV1HQu2K-1690882200220)(E:\Java笔记\数据结构\线形结构\链表结构\链表结构.assets\image-20230801092637927.png)]

以下就是双向链表的最基本单位

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K825DLOg-1690882200221)(E:\Java笔记\数据结构\线形结构\链表结构\链表结构.assets\image-20230801092801444.png)]

节点的前指针域指向前驱,后指针域执行后继

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6zQK3LeB-1690882200221)(E:\Java笔记\数据结构\线形结构\链表结构\链表结构.assets\image-20230801112807368.png)]

完整的双向链表

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QC8iOcYu-1690882200222)(E:\Java笔记\数据结构\线形结构\链表结构\链表结构.assets\image-20230801140702244.png)]

双向链表的功能

    • 向双向链表的尾节点之后添加节点

      • 尾节点的后指针域存储新添加节点的内存地址

      • 新添加节点的前指针域存储尾节点的内存地址

        注意:此时尾节点就是新添加的这个节点

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tIwTitAd-1690882200223)(E:\Java笔记\数据结构\线形结构\链表结构\链表结构.assets\20230801_142639.gif)]

    • 向双向链表的首元节点之前添加节点

      • 新添加节点的后指针域存储首元节点的内存地址

      • 首元节点的前指针域存储新添加节点的内存地址

        注意:此时首元节点就是新添加的这个节点

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yqVLrOWP-1690882200223)(E:\Java笔记\数据结构\线形结构\链表结构\链表结构.assets\20230801_153607.gif)]

    • 删除中间节点时修改改节点的前驱和后继的指针方向即可

      注意:被删除的节点称之为:野节点,这并不是真正意义上的删除,它在内存中依旧存在。那么野节点的最终归宿是被JVM的GC(垃圾回收器)所回收,也就是释放该节点的内存空间,这才是真正意义上的删除

      额外知识:java中的垃圾回收器(Garbage Collector,GC)负责管理内存的分配和释放。当一个对象没有任何引用指向它时,它就变得不可达,而垃圾回收器会将其标记为垃圾对象,并在适当的时候回收该对象所占用的内存空间。这个过程称为垃圾回收。

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kYeMLeLn-1690882200223)(E:\Java笔记\数据结构\线形结构\链表结构\链表结构.assets\20230801_160349 (1)].gif)

    • 挪动指针找到要修改的节点,之后讲修改节点的数据域中的数据修改掉
    • 挪动指针找到要所要查找的数据。

特点

  • 双向性:
    • 每个节点除了存储自己的数据外,还包含两个指针域,分别存储前驱和后继的内存地址,因此可以在链表中向前或向后遍历。
  • 动态性:
    • 双向链表可以在运行时动态地添加、删除和修改节点,相对于数组等静态数据结构更加灵活。
  • 插入和删除操作高效:
    • 由于双向链表中的节点包含指向前驱和后继的的指针,因此插入和删除操作只需要调整节点前后的指针,而不需要像数组那样移动大量元素。
  • 空间利用率较高:
    • 相比单向链表,双向链表需要额外的指针来表示前一个节点,空间利用率略低一些,但在某些情况下方便了一些操作。
  • 双向遍历能力:
    • 双向链表可以从任意节点开始向前或向后遍历,这在某些场景中非常有用,例如需要反向迭代链表。

双向链表在内存开销上相对于单向链表稍高。

由于有两个指针,插入和删除操作涉及到更多的指针修改,相对于单向链表略微复杂。

单向链表和双向链表的区别

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nqluhh2h-1690882200224)(E:\Java笔记\数据结构\线形结构\链表结构\链表结构.assets\image-20230801170615367.png)]

单向链表相对于双向链表更简单且节省内存,适用于对内存占用敏感且只需要单向遍历的情况。

而双向链表由于具有双向遍历的能力,适用于需要频繁插入、删除和反向遍历的场景。

在选择使用哪种链表结构时,应根据具体需求权衡其优缺点。

MyList
public interface MyList<E> {
    //添加节点数据
    void add(E element);

    //获取节点数据
    E get (int index);

    //获取链表长度
    int size();

    //根据指针移除节点
    E remove(int index);

    //从头添加元素
    void addFirst(E element);
    
    //从尾添加元素
    void addLast(E element);
}

MyDoubleLinkedList
public class MyDoubleLinkedList<E> implements MyList<E> {

    private int size;//记录元素的个数

    private Node head ;// 记录头节点

    private Node tail;//记录尾节点
    /**
     * 向双向链表中添加元素数据
     * @param element
     */
    @Override
    public void add(E element) {
        this.linkLast(element);
    }

    /**
     * 将节点对象添加到双向链表的尾部
     * @param element
     */
    private void linkLast(E element){
        //获取尾节点
        Node t = this.tail;
        Node<E> node = new Node<>(t,element,null);

        //将新节点定义为尾节点
        this.tail = node;
        if (t == null){//当t等于空的时候说明这个链表是个空链表
            this.head=node;//将向的元素数据赋值到头节点
        }else{
            t.next=node;//否则将新元素赋值到尾节点之后
        }
        this.size++;//添加成功长度自增加一

    }


    /**
     * 根据指针获取对应的元素数据
     * @param index
     * @return
     */
    @Override
    public E get(int index) {
        //对index做合法性校验
        this.rangeCheck(index);
        Node<E> node = this.getNode(index);
        return node.item;
    }

    /**
     * 判断当前index的合法性
     */
    private void rangeCheck(int index){
        if(!(index >=0 && index < this.size)){
            int a = this.size-1;
            throw new IndexOutOfBoundsException("您输入的索引为:"+index+"它的指针长度为:"+a);
        }
    }

    /**
     * 根据位置获取指定的节点对象
     * @param index
     * @return
     */
    private Node getNode(int index){
        //判断当前输入的指针距离头或者尾哪个节点近
        //如果输入的指针大,从尾部开始找,
        //如果输入的指针小,从头部开始找

        if (index < (this.size >> 1)){
            Node node = this.head;
            //遍历指针
            for(int i =0; i < index;i++){
                //拿到指针处的下一个节点对象赋值node
                node=node.next;
            }
            return node;
        }else {
            Node node = this.tail;
            //从尾节点找指针
            for (int i = this.size-1; i>index; i--){
                node = node.prev;
            }
            return node;
        }
    }

    /**
     * 获取元素的长度(个数)
     * @return
     */
    @Override
    public int size() {
        return this.size;
    }

    /**
     * 根据指定指针删除元素
     * @param index
     * @return
     */
    @Override
    public E remove(int index) {
        //对index指针进行合法性校验
        this.rangeCheck(index);

        //根据index进行获取节点对象,判断从头删还是从尾部删除
        Node<E> node = this.getNode(index);

        //获取index指针处的节点元素数据
        E item = node.item;

        //判断当前节点是否尾头节点
        if (node.prev == null){
            this.head = node.next;
        }else {
            //完成当前节点的直接前驱节点和当前节点的直接后继节点,挂接
            node.prev.next = node.next;
        }
        //当前节点断掉与它直接前驱点的连接
        node.prev = null;
        //当前节点断掉与他直接后继节点的连接
        node.next =null;
        node.item =null;

        //长度自减一
        this.size--;

        //返回被删除的节点元素数据
        return item;
    }

    //从头开始添加元素
    @Override
    public void addFirst(E element) {
        this.linkFirst(element);
    }

    //从尾部添加元素
    @Override
    public void addLast(E element) {
        this.linkLast(element);
    }






    //在链表的头添加元素
    private void linkFirst(E element){
        //获取头节点对象
        Node head = this.head;
        Node node = new Node(null,element,head);

        //将新节点定义为头节点
        this.head = node;

        //判断当前链表中是否有节点,如果没有该节则是头节点也是尾节点
        if(this.head == null){
            this.tail = node;
        }else {
            this.head.prev = node;
        }
        this.size++;//记录链表长度

    }

    /**
     * 创建节点类
     * @param <E>
     */
    class Node<E> {
        private E item;//记录元素
        private Node<E> next; // 下一个节点对象
        private Node<E> prev; // 上一个节点对象

        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }
}

测试
public class MyLinkedMain {
    public static void main(String[] args) {
        MyDoubleLinkedList linkedList = new MyDoubleLinkedList();

        linkedList.add(12);
        linkedList.add(13);
        linkedList.add(14);
        linkedList.add(15);
        linkedList.add(16);
        System.out.println(linkedList.get(3));//3是指针,从尾部开始找
        System.out.println(linkedList.get(4));//4是指针,从尾部开始找
        System.out.println(linkedList.get(1));//1是指针,从头部开始找
        System.out.println(linkedList.size());//获取linkedList长度
        System.out.println("删除前的指针1处的数据"+linkedList.get(1));
        System.out.println(linkedList.remove(1));
        System.out.println("删除后的指针1处的数据"+linkedList.get(1));

        System.out.println("删除前的指针0处的数据"+linkedList.get(4));
        System.out.println(linkedList.remove(4));
        try {
            System.out.println("删除后的指针0处的数据"+linkedList.get(4));

        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            System.out.println("删除后的链表长度"+linkedList.size());//获取linkedList长度
        }
    }
}

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

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

相关文章

Java开发中的------修改密码+忘记密码

目录 1.修改密码 客户端响应 前端vue 后端 controller层 ServiceImpl实现层 2.忘记密码 客户端响应 后端 controller层 serviceImpl实现层 本章需要准备&#xff1a;springcloud项目&#xff0c;依赖&#xff0c;数据库.... 数据库SQL SET FOREIGN_KEY_CHECKS0;-- -…

C++设计模式之责任链设计模式

C责任链设计模式 什么是责任链设计模式 责任链设计模式是一种行为型设计模式&#xff0c;它允许多个处理请求的对象串联起来&#xff0c;形成一个处理请求的链。每个对象都有机会处理请求&#xff0c;如果该对象不能处理请求&#xff0c;则将请求传递给链中的下一个对象。 该…

Inkscape 1.3 版开放源代码 SVG 编辑器发布,新增形状生成器工具和许多更改

导读Inkscape 是功能强大的开源、跨平台、免费 SVG&#xff08;可缩放矢量图形&#xff09;编辑器&#xff0c;今天已更新到稳定的 1.3 版&#xff0c;这是一个引入新功能和许多改进的重要版本。 Inkscape 1.3 是在 Inkscape 1.2 发布一年零两个月后推出的&#xff0c;它引入了…

[pymc3][python]pymc3安装后测试代码2

测试环境&#xff1a; pymc33.11.2 代码&#xff1a; import numpy as np import pymc3 as pm import matplotlib.pyplot as pltif __name__ __main__:# 生成随机数据np.random.seed(123)x np.linspace(0, 1, 100)y 0.5 * x np.random.normal(0, 0.1, size100)# 定义概率…

C/C++算法——散列表

1、散列表介绍 散列表的英文叫Hash Table&#xff0c;我们平时也叫它哈希表或者Hash 表。散列表用的是数组支持按照下标随机访问数据的特性&#xff0c;所以散列表其实就是数组的一种扩展&#xff0c;由数组演化而来。可以说&#xff0c;如果没有数组&#xff0c;就没有散列表。…

【C++】-二叉搜索树的详解(递归和非递归版本以及巧用引用)

&#x1f496;作者&#xff1a;小树苗渴望变成参天大树&#x1f388; &#x1f389;作者宣言&#xff1a;认真写好每一篇博客&#x1f4a4; &#x1f38a;作者gitee:gitee✨ &#x1f49e;作者专栏&#xff1a;C语言,数据结构初阶,Linux,C 动态规划算法&#x1f384; 如 果 你 …

JDBC案例

文章目录 案例1 &#xff1a;修改数据库中的数据案例2&#xff1a;查询数据库中的数据案例3&#xff1a;查询数据库中账户表数据&#xff0c;并将其封装成Account对象&#xff0c;&#xff0c;存储到ArrayList集合当中案例4&#xff1a;商品的增删改查1.准备环境2.查询3.添加4.…

1.微信小程序开发-快速上手

1.环境搭建 1.1 下载开发者工具 微信开发者工具下载地址与更新日志 | 微信开放文档微信开发者平台文档https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html 1.2 注册小程序 获取小程序AppId 2.小程序组件 1.view&#xff1a;用于展示视图元素&#x…

WAF绕过-权限控制篇-后门免杀

WAF绕过主要集中在信息收集&#xff0c;漏洞发现&#xff0c;漏洞利用&#xff0c;权限控制四个阶段。 1、什么是WAF&#xff1f; Web Application Firewall&#xff08;web应用防火墙&#xff09;&#xff0c;一种公认的说法是“web应用防火墙通过执行一系列针对HTTP/HTTPS的安…

所有集群启动的命令

所有集群启动的命令 查询所有节点启动Hadoop集群(Yarn模式)关闭Hadoop集群Spark&#xff08;local模式&#xff09;启动Spark集群standalone模式(不用了)关闭standalone模式HA下的standalone模式关闭HA-standalone模式Yarn模式&#xff08;重点&#xff09; 关闭Spark集群启动f…

接口/Web自动化测试如何做?框架如何搭建封装?

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 自动化测试怎么做…

数据可视化(5)热力图及箱型图

1.热力图 #基本热力图 #imshow&#xff08;x&#xff09; #x&#xff0c;数据 x[[1,2],[3,4],[5,6],[7,8],[9,10]] plt.imshow(x) plt.show() #使用热力图分析学生的成绩 dfpd.read_excel(学生成绩表.xlsx) #:表示行号 截取数学到英语的列数 xdf.loc[:,"数学":英语].…

技能生态链职业技能等级评价认定业务

一、项目背景 根据《关于公布广东省2022年第一批职业技能等级认定社会培训评价组织名单的通知》&#xff08;粤人社函〔2022〕76号&#xff09;&#xff0c;广东泰迪智能科技股份有限公司于2022年入选广东省2022年第一批职业技能等级认定社会评价组织&#xff0c;并根据《关于…

《水经注地图服务》发布的影像数据如何在OsgEarth中调用

OsgEarth 是一个用于OpenSceneGraph (OSG)的可扩展地形渲染工具包&#xff0c;它是一个开源、高性能、3D 图形工具包。 只需创建一个简单的 XML 文件&#xff0c;将其指向您的图像、高程和矢量数据&#xff0c;将其加载到您最喜欢的 OSG 应用程序中&#xff0c;然后开始&#…

蓝桥云课ROS机器人旧版实验报告-05导航功能

项目名称 实验五 导航功能 成绩 内容&#xff1a;创建变换、发布传感器消息、里程数据信息、创建基础控制器、创建地图&#xff0c;机器人配置、全局和局部代价地图、rviz详细配置、自适应蒙特卡洛定位&#xff0c;避障&#xff0c;目标发送 实验记录&#xff08;70分&…

Jenkins工具系列 —— 插件 实现用户权限分配与管理

文章目录 安装插件 Role-based Authorization Strategy添加用户注册配置权限查看当前使用者&#xff0c;获取user id配置管理员权限配置普通用户权限&#xff08;非管理员权限&#xff09; 小知识 安装插件 Role-based Authorization Strategy 点击 左侧的 Manage Jenkins —&…

[LeetCode]只出现一次的数字相关题目(c语言实现)

文章目录 LeetCode136. 只出现一次的数字ⅠLeetCode137. 只出现一次的数字 IILeetCode260. 只出现一次的数字 IIILeetCode268. 丢失的数字 LeetCode136. 只出现一次的数字Ⅰ 题目: 给你一个 非空 整数数组 nums &#xff0c;除了某个元素只出现一次以外&#xff0c;其余每个元…

使用MyBatis(2){使用myBatis操作增删改查/动态SQL}

目录 一、定义接口、实体类、创建XML文件实现接口&#xff09; 二、MyBatis的增删改查 &#x1f345;1、MyBatis传递参数查询 &#x1f388;写法一 &#x1f388;写法二 &#x1f388;两种方式的区别 &#x1f345;2、删除操作 &#x1f345;3、根据id修改用户名 &#…

Java 基础进阶总结(一)反射机制学习总结

文章目录 一、初识反射机制1.1 反射机制概述1.2 反射机制概念1.3 Java反射机制提供的功能1.4 反射机制的优点和缺点 二、反射机制相关的 API2.1 一、初识反射机制 1.1 反射机制概述 JAVA 语言是一门静态语言&#xff0c;对象的各种信息在程序运行时便已经确认下来了&#xff0…

延长周末体验感

延长周末体验感 写在最前面周末的时间规划题外话善解人意的chatgpt 提升周末体验感的好方法随机选择一个周末活动 怎样才能获得充分的休息 写在最前面 话题征文~ https://activity.csdn.net/creatActivity?id10533&spm1011.2432.3001.9644 工作以后常常容易感到疲于奔命…