【数据结构】—— 双链表的增删改查

news2025/8/12 9:52:24

❤️一名热爱Java的大一学生,希望与各位大佬共同学习进步❤️

🧑个人主页:@周小末天天开心

各位大佬的点赞👍 收藏⭐ 关注✅,是本人学习的最大动力

感谢!

📕该篇文章收录专栏—数据结构

目录

双链表

单链表和双链表的区别

双链表的增删改查

定义HeroNode2类,用来存放属性

创建DoubleLinkedList类用来存放方法

添加节点到链表最后

按照编号的顺序添加节点

显示双向链表

修改链表中的节点信息

删除节点信息

编写DoubleLinkedListDemo类进行演示


双链表

        双链表也叫双向链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。

单链表和双链表的区别

(1)单链表查找的方向只能是一个方向,而双链表可以向前或者向后查找

(2)单链表不能自我删除,需要依靠辅助节点,而双链表可以自我删除(单链表删除时,总是要找到辅助节点temp,temp是待删除节点的前一个节点)

(3)双链表比单链表多了一个 pre 属性,用来指向前一个节点,默认为null


双链表的增删改查

定义HeroNode2类,用来存放属性

//定义HeroNode2 ,每个HeroNode2 对象就是一个节点
class HeroNode2 {
    public int no;
    public String name;
    public String nickname;//昵称
    public HeroNode2 next;//指向下一个节点,默认为null
    public HeroNode2 pre;//指向前一个节点,默认为null

    //构造器
    public HeroNode2(int no, String name, String nickname) {
        this.no = no;
        this.name = name;
        this.nickname = nickname;
    }

    //为了显示方法,这里重写 toString() 方法
    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                ", nickname='" + nickname + '\'' + "}";
    }
}

创建DoubleLinkedList类用来存放方法

添加节点到链表最后

(1)先创建一个 head头节点,作用就是表示单链表的头

(2)找到双向链表的最后一个节点

(3)temp.next = heroNode;

(4)heroNode.pre = temp;

//创建一个双向链表的类
class DoubleLinkedList {

//先初始化一个头节点,头节点不要动,如果改动头节点那么就不好去找到链表最顶端的节点了
    private HeroNode2 head = new HeroNode2(0, "", "");
    //头节点不存放具体的数据

    public HeroNode2 getHead() {//返回头节点
        return head;
    }

    //添加数据到双链表最后
    public void add(HeroNode2 heroNode) {
        //因为head头节点不能动,所以需要一个辅助变量temp
        HeroNode2 temp = head;//将temp指向head
        //遍历链表,找到链表最后
        while (true) {
            //当temp的域等于空时,说明temp找到了链表最后了
            if (temp.next == null) {//找到最后了,结束程序
                break;
            }
            //如果没有找到最后,就将temp 后移指向下一个数据
            temp = temp.next;
        }
        //当退出了 while 循环时,那么 temp 就指向了链表的最后
        temp.next = heroNode; //将最后这个节点的next域指向新的节点
        heroNode.pre = temp;//将最后这个节点指向前一个节点,构成双链表
    }
}

按顺序添加到链表后演示

 

按照编号的顺序添加节点

//根据排名将英雄插入到指定位置
    public void addByOrder(HeroNode2 heroNode) {
        //因为头节点不能动,因此需要通过一个辅助指针(变量)来帮助找到添加的位置
        //找到辅助变量,辅助变量temp应该是位于添加位置的前一个节点,否则无法插入
        HeroNode2 temp = head;
        boolean flag = false;//flag标志添加的编号是否存在,默认为false
        while(true) {
            if(temp.next == null) {//说明temp已经到了链表最后
                break;
            }
            if(temp.next.no > heroNode.no) {//位置找到,就在temp的后面插入
                //如果temp.next.no > heroNode.no
                //说明 heroNode 就应该插入到 temp 和 temp.next 之间
                break;
            }
            if(temp.next.no == heroNode.no) {
                //说明希望添加的heroNode的编号以及存在
                flag = true;
                break;
            }
            temp = temp.next;//如果三个条件都不符合就将 temp 后移,继续遍历
        }
        //判断flag的值
        if(flag) {
            //如果 flag == true。说明编号已存在,所以无法插入
            System.out.printf("准备插入的编号%d已经存在,无法插入",heroNode.no);
        } else {
            //否则可以插入到链表中,temp 的后面
            if(temp.next != null) {
                //如果要插入的节点在最后则不需要执行下面的语句
                heroNode.next = temp.next;
                temp.next.pre = heroNode;
            }
            temp.next = heroNode;
            heroNode.pre = temp;
        }
    }

显示双向链表

//显示双向链表[遍历]
    public void list() {
        //先判断链表是否为空
        if (head.next == null) { //说明了链表为空
            System.out.println("链表为空");
            return;
        }
        //如果链表不为空,且头节点不能动,因此需要一个辅助变量来遍历
        HeroNode2 temp = head.next;
        //因为已经判断了链表不为空,说明链表至少有一个数据,所以是 head.next(头节点指向的下一个数据)
        while (true) {
            //判断是否已经到了链表最后
            if (temp == null) {
                //遍历是到了链表最后,所以退出循环
                break;
            }
            //如果不为空,则输出该节点的信息
            System.out.println(temp);//已经重写了 toString()
            //输出后需要将temp后移一位,因为输出该信息后需要让temp指向下一位,输出下一位的信息
            temp = temp.next;//不后移就是一个死循环

        }
    }

修改链表中的节点信息

//修改节点的信息,根据no编号来修改,即no编号不能改
    //根据newHeroNew 的 no 来进行修改即可
    public void update(HeroNode2 newHeroNew) {
        //判断是否为空
        if (head.next == null) {
            System.out.println("该链表为空");
            return;
        }
        //修改节点的信息,根据no编号来修改,即no编号不能改
        //根据newHeroNew 的 no 来进行修改即可
        //可以看出双向链表的节点内容修改和单向链表一样
        //只是节点类型更改为了 HeroNode2
        HeroNode2 temp = head.next;//temp 赋的值为头节点的下一个节点
        boolean flag = false;//表示是否找到了该节点
        while (true) {
            if (temp == null) {
                //说明链表已经遍历完成,因为temp 指向的是下一个节点(需要注意,和上面不同)
                break;
            }
            if (temp.no > newHeroNew.no) {
                //因为temp 指向的是下一个节点。
                //所以就不需要 temp.next.no 了(需要注意,和上面不同)
                //位置找到,就在temp后面进行添加
                break;
            }
            if (temp.no == newHeroNew.no) {
                //因为temp 指向的是下一个节点。
                //所以就不需要 temp.next.no 了(需要注意,和上面不同)
                //说明希望添加的编号已经存在
                flag = true;
                break;
            }
            temp = temp.next;//如果循环没有结束就一直进行遍历
        }
        //根据 flag 来进行判断是否找到了要修改的节点
        if (flag) {//如果flag 为 true 说明就找到了要修改的节点
            temp.name = newHeroNew.name;
            temp.nickname = newHeroNew.nickname;
            //注意:no 编号不可以修改
        } else { //说明没有找到要修改的节点
            System.out.printf("没有找到编号等于%d的节点\n", newHeroNew.no);
        }
    }

修改链表中的节点信息演示

 

删除节点信息

(1)因为是双向链表,所以我们可以自我删除某个节点

(2)直接找到要删除的节点 temp

(3)temp.pre.next = temp.next;

(4)temp.next.pre = temp.pre;(如果是删除最后一个节点就不需要,否则会报空指针异常)

(5)被删除的节点,将不会有其他引用指向,会被垃圾回收机制回收

//删除节点
    /*
    思路
    1.head 节点不能动,因此我们需要一个 temp 辅助接点找到待删除的前一个节点
    2.说明我们在比较时,是 temp.next.no 和需要删除的节点的 no 比较
     */
    public void delete(int no) {
        //判断链表是否为空
        if (head.next == null) {
            System.out.println("链表为空,无法删除");
            return;
        }
        HeroNode2 temp = head.next;//辅助节点
        boolean flag = false;//标志是否找到了待删除的节点
        while (true) {
            if (temp == null) {//已经到链表的最后了
                break;
            }
            if (temp.no == no) {
                //找到了待删除节点temp
                flag = true;
                break;
            }
            temp = temp.next;//让 temp 后移,实现遍历
        }
        //判断 flag
        if (flag) {
            //如果flag为真,找到了要删除的节点,可以删除
            temp.pre.next = temp.next;
            //如果是删除最后一个节点就不需要执行下面这句话,否则会空指针异常
            if (temp.next != null) {
                temp.next.pre = temp.pre;
            }
        } else {
            System.out.printf("要删除的%d节点不存在\n", no);
        }
    }

删除节点信息演示


编写DoubleLinkedListDemo类进行演示

public class DoubleLinkedListDemo {
    public static void main(String[] args) {

        //测试双链表
        System.out.println("=====双链表的测试=====");

        //先创建节点
        HeroNode2 hero1 = new HeroNode2(1, "宋江", "及时雨");
        HeroNode2 hero2 = new HeroNode2(2, "卢俊义", "玉麒麟");
        HeroNode2 hero3 = new HeroNode2(3, "吴用", "智多星");
        HeroNode2 hero4 = new HeroNode2(4, "林冲", "豹子头");

        //创建双向链表
        DoubleLinkedList doubleLinkedList = new DoubleLinkedList();

        //添加双链表中的数据到最后
        doubleLinkedList.add(hero1);
        doubleLinkedList.add(hero2);
        doubleLinkedList.add(hero3);
        doubleLinkedList.add(hero4);
        //显示双链表
        doubleLinkedList.list();

//        //插入双链表中的数据
//        doubleLinkedList.addByOrder(hero2);
//        doubleLinkedList.addByOrder(hero4);
//        doubleLinkedList.addByOrder(hero3);
//        doubleLinkedList.addByOrder(hero1);
//        //显示双链表
//        doubleLinkedList.list();

//        //测试修改节点后的链表
//        System.out.println("\n=====修改后的链表为=====");
//        HeroNode2 newHeroNode = new HeroNode2(4, "公孙胜", "入云龙");
//        doubleLinkedList.update(newHeroNode);
//        //显示修改后的双链表
//        doubleLinkedList.list();

//        //删除双链表中的节点
//        doubleLinkedList.delete(1);
//        doubleLinkedList.delete(4);
//        //显示删除后的双链表
//        System.out.println("\n=====删除后的双链表=====");
//        doubleLinkedList.list();
    }
}

 

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

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

相关文章

艾美捷小鼠肿瘤坏死因子α-ELISpot试剂盒使用指南

ELISpot Plus for enumeration of cells secreting TNF-α This kit is ideal for users who want a convenient and sensitive assay. The assay is designed for the enumeration of cells secreting mouse TNF-α. The kit includes ELISpot plates pre-coated with monocl…

[附源码]计算机毕业设计JAVA面试刷题系统

[附源码]计算机毕业设计JAVA面试刷题系统 项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis M…

【C++】--模拟实现vector

文章目录Constructors(构造函数)myvector()myvector(int n, const T& val T())myvector(InputIterator first, InputIterator last)拷贝构造交换函数myvector< T >& operator(myvector< T > v)迭代器扩容reserveresize插入和删除push_backpop_backinserte…

CDH启用kerberos 高可用运维实战

一、背景说明 在前的文章中介绍过《CDH集成的kerberos迁移实战》,由此也考虑到kerberos单节点可能引发的线上事故&#xff0c;所有考虑到把线上kerberos服务启用高可用。 二、环境介绍 系统版本 CentOS Linux release 7.6.1810 (Core) CM版本 Kerberos版本 三、实操…

桌面应用开发有哪些主流框架?

受益于开源技术的发展&#xff0c;以及响应快速开发的实际业务需求&#xff0c;跨平台开发不仅限于移动端跨平台&#xff0c;桌面端虽然在市场应用方面场景不像移动端那么丰富&#xff0c;但也有市场的需求。 相对于个人开发者而言&#xff0c;跨平台框架的使用&#xff0c;主要…

零基础学习下载FL Studio2023水果编曲软件

FL Studio工具常称水果编曲软件&#xff0c;是一款功能强大的编曲软件&#xff0c;集编曲&#xff0c;录音&#xff0c;剪辑&#xff0c;混音于一身&#xff0c;简单易上手&#xff0c;灵活性高&#xff0c;强大到突破想象。FL Studio&#xff0c;当前版本 FL Studio21&#xf…

Transductive Learning 和 Inductive Learning

简介 在 kipf-GCN 和 GraphSage 中&#xff0c;对 Transductive Learning 和 Inductive Learning 有了比较深刻的认识。 kipf-GCN 在其论文中提到算法属于 transductive node classification&#xff0c;也就是在训练节点embedding的时候要看到全图的节点&#xff0c;这是因为…

Linux——进程间通信(共享内存)

一、共享内存 1、定义 共享内存为多个进程之间共享和传递数据提供了一种有效的方式。共享内存是先在物理内存上申请一块空间&#xff0c;多个进程可以将其映射到自己的虚拟地址空间中。所有进程都可以访问共享内存中的地址&#xff0c;就好像它们是由malloc分配的一样。如果某…

47 - 父子间的冲突

---- 整理自狄泰软件唐佐林老师课程 1. 思考 子类中是否可以定义父类中的同名成员&#xff1f; 如果可以&#xff0c;如何区分&#xff1f;如果不可以&#xff0c;why&#xff1f; 1.1 编程实验&#xff1a;同名成员变量 #include <iostream> #include <string>…

操作系统导论--受限制的直接执行

受限直接执行 为了使程序尽可能快地运行&#xff0c;操作系统开发人员想出了一种技术——我们称之为受限的直接执行。 这个概念的“直接执行”部分很简单&#xff1a;只需直接在CPU上运行程序即可。因此&#xff0c;当OS希望启动程序运行时&#xff0c;它会在进程列表中为其创…

Packet Tracer - 排除单区域 OSPFv2 故障

地址分配表 设备 接口 IP 地址 子网掩码 默认网关 R1 G0/0 172.16.1.1 255.255.255.0 不适用 S0/0/0 172.16.3.1 255.255.255.252 不适用 S0/0/1 192.168.10.5 255.255.255.252 不适用 R2 G0/0 172.16.2.1 255.255.255.0 不适用 S0/0/0 172.16.3.2 25…

slam定位学习笔记(六)

学习内容来自这篇文章&#xff0c;主要是将模块进行划分以及根据划分的模块对前面的代码结构进行调整。 一、模块划分 之前的文章主要介绍了这几个功能的实现&#xff1a;传感器时间同步、点云去畸变、实时显示点云、精度评价和前端里程计。将它们区分成三个模块&#xff1a;…

基于JSP的私人牙科医院管理系统【数据库设计、源码、开题报告】

数据库脚本下载地址&#xff1a; https://download.csdn.net/download/itrjxxs_com/86466989 主要使用技术 ServletJSPJSCSSMysql 功能介绍 登录注册模块&#xff1a;填写基本信息注册&#xff08;普通用户&#xff09;登录&#xff1b; 公告管理&#xff1a;公告增删改查&…

预训练模型分词方式

BPE、WordPiece、SentencePiece tokenize的目标是将输入的文本流&#xff0c; 切分成一个个子串&#xff0c;使得每个子串具有相对完整的语义&#xff0c;便于学习embedding表达和后续模型的使用。 tokenize三种粒度&#xff1a;word、subword、char word/词&#xff1a;最自…

Flutter高仿微信-第35篇-单聊-视频通话

Flutter高仿微信系列共59篇&#xff0c;从Flutter客户端、Kotlin客户端、Web服务器、数据库表结构、Xmpp即时通讯服务器、视频通话服务器、腾讯云服务器全面讲解。 详情请查看 效果图&#xff1a; 目前市场上第三方视频接口的价格高的吓人 视频通话价格&#xff1a; 标清&…

代码随想录算法训练营day55 | 392.判断子序列,115.不同的子序列

392.判断子序列 参考代码随想录算法训练营第五十五天 |392. 判断子序列、115. 不同的子序列 - 掘金 暴力解法&#xff1a; Time Complexity: O(M*N) Space Complexity: O(N) class Solution:def isSubsequence(self, s: str, t: str) -> bool:start 0 #used to make sur…

[附源码]java毕业设计医院网上预约系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Docker(9)DockerFile

文章目录什么是DockerFile构建过程执行DockerFile大致流程常用保留字DockerFile小案例创建dockerfile文件dockerfile内部脚本执行命令虚悬镜像什么是DockerFile DockerFile 使用来构建Docker镜像的文本文件&#xff0c;是由一条条构建镜像所需要的指令和参数组成脚本 构建过程…

CSDN Markdown 编辑器的目录与页内跳转功能

CSDN Markdown 编辑器的目录与页内跳转功能 文章目录CSDN Markdown 编辑器的目录与页内跳转功能 目录 页内跳转 正文内容跳转到标题正文内容跳转到正文内容结语目录 CSDN Markdown 编辑器的目录功能使用[TOC](你想要显示的目录总标题)格式&#xff0c;其中默认可省略括号及括…

2022-11-06 网工进阶(三十五)---PIM-SM工作原理(组播分发树的形成、ASM、SSM)

PIM-DM的局限性 中大型组播网络中由于网络较大&#xff0c;如果依然使用PIM-DM会遇到诸多问题&#xff1a; 1 使用“扩散-剪枝”方式需要全网扩散组播报文&#xff0c;对于网络有一定冲击。 2 所有组播路由器均需要维护组播路由表&#xff0c;即使该组播路由器无需转发组播数…