数据结构与算法—链表之单链表

news2024/9/22 6:40:09

文章目录

  • 链表
    • 单链表
      • 结构和特点
      • 创建添加
      • 修改
      • 删除

2023年的第一篇文章

在开发过程中,选择合适的数据结构是很重要的,可以快速处理数据的存储及使用问题。计划有时间慢慢系统的学习《数据结构与算法》,看看视频,练习实践,最后用于工作中。

链表

单链表

结构和特点

链表是有序的列表,在内存中的存储结构如下:

特点:

  • 以节点的方式存储,是链式存储
  • 每个节点包含data域(用于存放数据),next域(指向下一个节点)
  • 链表的各个节点在内存中不一定是连续存储的
  • 链表分为带head节点和不带head节点的

链表的逻辑结构:

创建添加

因为链表是以节点存储,在创建链表时,就根据节点一个个添加连接起来。需要先创建一个head节点,根据head节点指向下一个节点,一个个串起来。

1、创建节点类

class HeroNode {
    // 英雄编号
    public int no;
    // 英雄名称
    public String name;
    // 英雄昵称
    public String nickName;
    // 用于指向下一个节点
    public HeroNode next;

    public HeroNode(int no, String name, String nickName) {
        this.no = no;
        this.name = name;
        this.nickName = nickName;
    }

    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                ", nickName='" + nickName + '\'' +
                '}';
    }
}

2、有了节点类后,就可以创建head头结点,然后根据head节点,进行节点添加。

有两种添加方法,一种是顺序添加,另一种是根据英雄编号大小排序添加。

顺序添加:
在这里插入图片描述

// 创建一个单链表类,并添加操作
class SingleLickedList {
    // 创建头结点
    private HeroNode head = new HeroNode(0, "", "");

    // 添加节点到单链表
    public void add(HeroNode heroNode) {
        // 因为head节点不能动,需要一个辅助指针进行遍历 temp
        HeroNode temp = head;
        // 遍历链表,找到最后一个节点,添加新节点
        while (true) {
            // 找到最后一个节点,因为最后的节点的next域为NULL
            if (temp.next == null) {
                break;
            }
            // 没有找到,temp后移
            temp = temp.next;
        }

        // 当退出while 循环时,temp 就指向了最后一个节点
        temp.next = heroNode;
    }
}

有了添加操作,为了方便测试,需要能够遍历链表,打印出里面的数据。

在SingleLickedList 类中添加方法list

public void list() {
    // 判断链表是否为空
    if (head.next == null) {
        System.out.println("链表为空");
        return;
    }

    HeroNode temp = head.next;
    while (true) {
        if (temp == null) {
            break;
        }
        // 打印节点
        System.out.println(temp);
        // 指针后移
        temp = temp.next;
    }
}

测试:

public static void main(String[] args) {
    // 创建节点
    HeroNode hero1 = new HeroNode(1, "宋江", "及时雨");
    HeroNode hero2 = new HeroNode(3, "李逵", "黑旋风");
    HeroNode hero3 = new HeroNode(2, "林冲", "豹子头");

    SingleLickedList list = new SingleLickedList();
    list.add(hero1);
    list.add(hero2);
    list.add(hero3);
    list.list();
}

结果:
在这里插入图片描述

可以看到正常添加完节点,并且按照添加的顺序显示。

有序添加:

就是在添加节点之前,查询是否有相同节点存在,如果存在,就不添加;查询下一个节点的编号no 是否大于需要添加节点的编号no,大于就表示要添加的节点就在下一个节点之前。

在这里插入图片描述

也就是根据节点的no ,有序的添加,创建方法 add2

// 创建一个单链表类,并添加操作
class SingleLickedList {
    // 头结点
    private HeroNode head = new HeroNode(0, "", "");

    public void add2(HeroNode heroNode) {
        HeroNode temp = head;
        // 表示该节点是否找到相同编号的节点存在,false:未找到,true:找到
        boolean flag = false;
        while (true) {
            if (temp.next == null) {
                //
                break;
            }
            if (temp.next.no > heroNode.no) {
                // 找到了位置,应该放在temp之后
                break;
            } else if (temp.next.no == heroNode.no) {
                // 已经有相同节点
                flag = true;
                break;
            }
            // 指针后移
            temp = temp.next;
        }

        if (flag) {
            System.out.printf("待加入的英雄编号:%d已经有了", heroNode.no);
        } else {
            // 加入
            heroNode.next = temp.next;
            temp.next = heroNode;
        }
    }

    // 添加节点到单链表
    public void add(HeroNode heroNode) {
        // 因为head节点不能动,需要一个辅助指针进行遍历 temp
        HeroNode temp = head;
        // 遍历链表,找到最后一个节点,添加新节点
        while (true) {
            // 找到最后一个节点,因为最后的节点的next域为NULL
            if (temp.next == null) {
                break;
            }
            // 没有找到,temp后移
            temp = temp.next;
        }

        // 当退出while 循环时,temp 就指向了最后一个节点
        temp.next = heroNode;
    }

    public void list() {
        // 判断链表是否为空
        if (head.next == null) {
            System.out.println("链表为空");
            return;
        }

        HeroNode temp = head.next;
        while (true) {
            if (temp == null) {
                break;
            }
            // 打印节点
            System.out.println(temp);
            // 指针后移
            temp = temp.next;
        }
    }
}

测试:

public static void main(String[] args) {
    // 创建节点
    HeroNode hero1 = new HeroNode(1, "宋江", "及时雨");
    HeroNode hero2 = new HeroNode(3, "李逵", "黑旋风");
    HeroNode hero3 = new HeroNode(2, "林冲", "豹子头");

    SingleLickedList list = new SingleLickedList();
    list.add(hero1);
    list.add(hero2);
    list.add(hero3);
    list.list();

    System.out.println("-------------------------");
    SingleLickedList list2 = new SingleLickedList();
    list2.add2(hero1);
    list2.add2(hero2);
    list2.add2(hero3);
    list2.list();
}

在这里插入图片描述

可以看到add2 所执行的是按顺序的

修改

根据节点数据的编号no,修该英雄的昵称nickname,创建方法 update

// 创建一个单链表类,并添加操作
class SingleLickedList {
    // 头结点
    private HeroNode head = new HeroNode(0, "", "");

    // 更新节点
    public void update(HeroNode heroNode) {
        if (head.next == null) {
            System.out.println("链表为空");
            return;
        }
        HeroNode temp = head;
        boolean flag = false; //是否找到需要修改的节点
        while (true) {
            if (temp.next == null) {
                break; //已经遍历完链表
            }
            if (temp.next.no == heroNode.no) {
                flag = true;
                break;
            }
            temp = temp.next;
        }

        if (flag) {
            temp.next.nickName = heroNode.nickName;
        } else {
            System.out.println("没有相关节点");
        }
    }

    public void add2(HeroNode heroNode) {
        HeroNode temp = head;
        // 表示该节点是否找到相同编号的节点存在,false:未找到,true:找到
        boolean flag = false;
        while (true) {
            if (temp.next == null) {
                //
                break;
            }
            if (temp.next.no > heroNode.no) {
                // 找到了位置,应该放在temp之后
                break;
            } else if (temp.next.no == heroNode.no) {
                // 已经有相同节点
                flag = true;
                break;
            }
            // 指针后移
            temp = temp.next;
        }

        if (flag) {
            System.out.printf("待加入的英雄编号:%d已经有了", heroNode.no);
        } else {
            // 加入
            heroNode.next = temp.next;
            temp.next = heroNode;
        }
    }

    // 添加节点到单链表
    public void add(HeroNode heroNode) {
        // 因为head节点不能动,需要一个辅助指针进行遍历 temp
        HeroNode temp = head;
        // 遍历链表,找到最后一个节点,添加新节点
        while (true) {
            // 找到最后一个节点,因为最后的节点的next域为NULL
            if (temp.next == null) {
                break;
            }
            // 没有找到,temp后移
            temp = temp.next;
        }

        // 当退出while 循环时,temp 就指向了最后一个节点
        temp.next = heroNode;
    }

    public void list() {
        // 判断链表是否为空
        if (head.next == null) {
            System.out.println("链表为空");
            return;
        }

        HeroNode temp = head.next;
        while (true) {
            if (temp == null) {
                break;
            }
            // 打印节点
            System.out.println(temp);
            // 指针后移
            temp = temp.next;
        }
    }
}

测试:

public static void main(String[] args) {
        // 创建节点
        HeroNode hero1 = new HeroNode(1, "宋江", "及时雨");
        HeroNode hero2 = new HeroNode(3, "李逵", "黑旋风");
        HeroNode hero3 = new HeroNode(2, "林冲", "豹子头");

        SingleLickedList list2 = new SingleLickedList();
        list2.add2(hero1);
        list2.add2(hero2);
        list2.add2(hero3);
        list2.list();

        // 测试更新节点
        System.out.println("------------------------");
        // 无相关节点
        list2.update(new HeroNode(4, "宋江", "梁上好汉"));
        list2.list();

        System.out.println("------------------------");
        // 修改成功
        list2.update(new HeroNode(1, "宋江", "梁上好汉"));
        list2.list();
}

在这里插入图片描述

删除

在这里插入图片描述

// 创建一个单链表类,并添加操作
class SingleLickedList {
    // 头结点
    private HeroNode head = new HeroNode(0, "", "");

    // 删除节点    这里可以根据需求将参数HeroNode 设置为只传入编号no即可
    public void delete(HeroNode heroNode) {
        if (head.next == null) {
            System.out.println("链表为空");
            return;
        }

        HeroNode temp = head;
        boolean flag = false;
        while (true) {
            if (temp.next == null) {
                 break;     // 已遍历完链表
            }

            if (temp.next.no == heroNode.no) {
                // 找到要删除的节点
                flag = true;
                break;
            }

            temp = temp.next;       // 指针后移
        }

        if (flag) {
            temp.next = temp.next.next;
        } else {
            System.out.println("没有相关节点");
        }

    }

    // 更新节点
    public void update(HeroNode heroNode) {
        if (head.next == null) {
            System.out.println("链表为空");
            return;
        }
        HeroNode temp = head;
        boolean flag = false; //是否找到需要修改的节点
        while (true) {
            if (temp.next == null) {
                break; //已经遍历完链表
            }
            if (temp.next.no == heroNode.no) {
                flag = true;
                break;
            }
            temp = temp.next;
        }

        if (flag) {
            temp.next.nickName = heroNode.nickName;
        } else {
            System.out.println("没有相关节点");
        }
    }

    public void add2(HeroNode heroNode) {
        HeroNode temp = head;
        // 表示该节点是否找到相同编号的节点存在,false:未找到,true:找到
        boolean flag = false;
        while (true) {
            if (temp.next == null) {
                //
                break;
            }
            if (temp.next.no > heroNode.no) {
                // 找到了位置,应该放在temp之后
                break;
            } else if (temp.next.no == heroNode.no) {
                // 已经有相同节点
                flag = true;
                break;
            }
            // 指针后移
            temp = temp.next;
        }

        if (flag) {
            System.out.printf("待加入的英雄编号:%d已经有了", heroNode.no);
        } else {
            // 加入
            heroNode.next = temp.next;
            temp.next = heroNode;
        }
    }

    // 添加节点到单链表
    public void add(HeroNode heroNode) {
        // 因为head节点不能动,需要一个辅助指针进行遍历 temp
        HeroNode temp = head;
        // 遍历链表,找到最后一个节点,添加新节点
        while (true) {
            // 找到最后一个节点,因为最后的节点的next域为NULL
            if (temp.next == null) {
                break;
            }
            // 没有找到,temp后移
            temp = temp.next;
        }

        // 当退出while 循环时,temp 就指向了最后一个节点
        temp.next = heroNode;
    }

    public void list() {
        // 判断链表是否为空
        if (head.next == null) {
            System.out.println("链表为空");
            return;
        }

        HeroNode temp = head.next;
        while (true) {
            if (temp == null) {
                break;
            }
            // 打印节点
            System.out.println(temp);
            // 指针后移
            temp = temp.next;
        }
    }
}

测试:

public static void main(String[] args) {
        // 创建节点
        HeroNode hero1 = new HeroNode(1, "宋江", "及时雨");
        HeroNode hero2 = new HeroNode(3, "李逵", "黑旋风");
        HeroNode hero3 = new HeroNode(2, "林冲", "豹子头");

        // 测试删除
        SingleLickedList list = new SingleLickedList();
        list.add(hero1);
        list.add(hero2);
        list.add(hero3);
        System.out.println("删除数据前的链表~~~~~~");
        list.list();

        System.out.println("删除数据后的链表~~~~~~");
        list.delete(new HeroNode(3, "李逵", "黑旋风"));
        list.list();

        System.out.println("删除数据后的链表~~~~~~");
        list.delete(new HeroNode(1, "宋江", "及时雨"));
        list.delete(new HeroNode(2, "林冲", "豹子头"));
        list.list();
    }

在这里插入图片描述

删除完数据后,链表就为空

到这,暂时学习了单链表的增删改查。

可以看看我的个人博客:
网站:https://www.fuzm.wang / https://liwangc.gitee.io
—————————————————————————

作为初学者,很多知识都没有掌握,见谅,如有错误请指出,以期进步,感谢!。后续有新的学习,继续补充上来。

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

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

相关文章

国产FPGA应用--易灵思Programming Mode完全解析

本文介绍易灵思的几种配置模式,方便大家参考。 一、易灵思下载模式: 二、下载模式选择: 1、SPI Active mode 时序图如下: 2、SPI Passive Mode 时序图如下: SPI Active using JTAG Bridge 实际项目中,SPI…

锥度张力控制(收卷应用)

收卷、放卷应用系列文章可以参看下面的文章链接: 变频器简单张力控制(线缆收放卷应用)_RXXW_Dor的博客-CSDN博客_收放卷应用张力控制的开闭环算法,可以查看专栏的其它文章,链接地址如下:PLC张力控制(开环闭环算法分析)_RXXW_Dor的博客-CSDN博客。https://blog.csdn.ne…

excel筛选技巧:不用函数公式也能快速多对多查找

说到excel中的筛选,想必大家早已是了如指掌,不过增强版的筛选,你听说过吗?它可比普通的筛选厉害多了,不仅能实现excel中的一对多查找,就连复杂的多对多查找也不在话下! 其实是使用公式还是用其…

原子性 以及悲观锁, 乐观锁

1. 前言 今天这篇文章要详细的说说,什么叫原子性,以及如果不是原子性的话,怎么能保证原子性。 2. 原子性 先说下并发编程的三大特性:可见性, 有序性, 原子性。 无论是在什么语言,原子性都是非常重要的,既然…

elasticsearch 的基本操作多维度分享

目录 一、索引操作 二、映射操作 三、文档操作 elasticsearch 的基本操作多维度分享此篇正式分享,具体包括索引、映射、文档的相关处理,模拟生成环境,通过DSL语句和java的高级REST形式全方位展示给大家; 一、索引操作 1、创建…

2023超好用的Mac清理优化工具CleanMyMacX

CleanMymac X Mac版本,以一种全面的方式扫描Mac系统以允许垃圾隐藏,您只需要轻松单击左鼠标按钮即可清洁数字G的垃圾,这是如此简单。立即提高您的MAC速度。为Apple System计算机建造可以帮助用户清理多种类型的垃圾和其他恶意束,提…

Sentinel 控制台安装与详解

Sentinel 控制台包含如下功能: 查看机器列表以及健康情况:收集 Sentinel 客户端发送的心跳包,用于判断机器是否在线。监控 (单机和集群聚合):通过 Sentinel 客户端暴露的监控 API,定期拉取并且聚合应用监控信息,最终可…

AIGC + 任意应用情景组合,从技术层面给了大家体验不同领域的创作的机会

还在为学技术的时候面对一大堆教程苦恼?画画、剪辑、建模 ... 啥啥啥都想学 🤯AIGC 来解决!!每个人都有机会当五分钟艺术家!AIGC 究竟有多强大?简单用一个公式来概况 AIGC 的强大之处,就是 AIG…

Harbor 镜像仓库

目录 一、Harboar概述 1.1 什么是 Harbor 1.2 Harbor优势 1.3 Harbor构成 1.4 Harbor的误区 二、Harbor 安装(http) 2.1 两种方式 2.2 具体安装步骤 2.2.1 先安装Docker和Docker Compose 2.2.2 下载Harbor 2.2.3 harbor.yml 的hostname 2.2.…

梳理一下我在2022年读过的23本书

作者 | gongyouliu编辑 | gongyouliu2022年我一共看了23本书,比自己之前定的目标——每年看36本书——少了不少。今天特意花大半天时间写一篇文章来整理一下今年看的书,梳理一下自己的思路,也算是留下一份记录。这份书单也希望给大家作为参考…

从这两道题重新理解,JS的this、作用域、闭包、对象

日常开发中,我们经常用到this。例如用Jquery绑定事件时,this指向触发事件的DOM元素;编写Vue、React组件时,this指向组件本身。对于新手来说,常会用一种意会的感觉去判断this的指向。以至于当遇到复杂的函数调用时&…

spring、mybatis、spring-mybatis、springboot-mybatis的配置文件

第一个Spring程序 第一个Mybatis程序 第一个Spring-Mybatis程序 第一个SpringBoot-Mybatis程序 1. Spring程序配置文件 beans.xml&#xff1a;/resources <!--注册一个Bean--><bean id"hello" class"com.kuang.pojo.Hello"><property nam…

03 I2C

特点 两根线 SCL SDA同步、半双工带数据应答支持总线挂载多设备 一主多从 多主多从 两个功能&#xff1a; 读取外设模块指定位置的寄存器写外设模块指定位置的寄存器一个完整的通讯协议一定是在软件和硬件上同时定义的。 硬件 所有的I2C设备的时钟线与数据线都要连在一起设备…

普元技术专家2022历史文章合集

大家好&#xff0c;新年快乐。‍献上2022年历史文章合集&#xff0c;方便你翻阅过去一年里的精华文章。【第一辑】企业服务总线&#xff08;ESB&#xff09;基于ESB的企业服务集成平台建设之道企业服务总线建设之道的探索与研究零代码能力干掉80%开发工作&#xff1a;普元ESB 8…

论文综述——Event-Event Relation Extraction using Probabilistic Box Embedding

Event-Event Relation Extraction using Probabilistic Box Embedding1 任务介绍2 Box Embedding3 BERE模型4 实验5 总结1 任务介绍 事件关系抽取&#xff1a;文本中包含多个事件e1,e2,…,en&#xff0c;识别每个事件对(ei,ej)之间的关系r(ei,ej) 子事件(Subevent)关系抽取&a…

(一)硬件制作--从零开始自制linux掌上电脑(F1C200S) <嵌入式项目>

一、工作环境及项目简介 立创EDA&#xff1a;硬件原理图及PCB绘制。 全志F1C200S&#xff1a;F1C100S内置32MB DDR1内存&#xff0c;F1C200S内置64MB DDR1内存。 原理图&#xff1a;参考开源项目&#xff0c;详见墨云&#xff0c; 详见peng-zhihui。 核心板&#xff1a;四层…

Java8 - Streams flatMap()

文章目录What is flatMap()?Why flat a Stream?DemoWhat is flatMap()? # Stream<String[]> # Stream<Stream<String>> # String[][][[1, 2],[3, 4],[5, 6] ]它由一个 2 级 Stream 或一个二维数组组成 。 在 Java 8 中&#xff0c;我们可以使用 flatMap…

前端工程师leetcode算法面试必备-二分搜索算法(下)

一、287. 寻找重复数 给定一个包含 n 1 个整数的数组 nums&#xff0c;其数字都在 1 到 n 之间&#xff08;包括 1 和 n&#xff09;&#xff0c;可知至少存在一个重复的整数。假设只有一个重复的整数&#xff0c;找出这个重复的数。 1、HashMap 在没有其它附加条件的情况下&…

直属领导和老板都给了我绩效A,HR最后审核时降成了B,平时没有得罪她,她为什么这么做?...

绩效考核时&#xff0c;谁才是最终决定你绩效那个人&#xff1f;一位网友最近遇到了这样一件糟心事&#xff1a;季度绩效&#xff0c;直属领导和老板都给了A&#xff0c;人事最后审核时给降成了B&#xff0c;平时和人事没什么工作交集&#xff0c;按理说没有得罪她&#xff0c;…

Java工具——MySQL介绍与安装

Java工具——MySQL介绍与安装MySQL数据库介绍1. 简介2. MySQL发展历史MySQL数据库安装1. 下载安装包2. 安装3. 配置环境变量MySQL数据库介绍 1. 简介 MySQL是应用最广泛、普及度最高的开源关系型数据库&#xff0c;MySQL由瑞典MySQL AB公司开发&#xff0c;目前属于Oracle旗下…