Java实现单链表及其基本操作

2023-05-16

目录

什么是单链表?

带头结点的单链表

不带头结点的单链表

模拟实现不带头结点的单链表

定义结点类

初始化 

头插法创建单链表 

尾插法创建单链表 

打印单链表 

单链表的查找

获取单链表的长度 

按位置寻找前驱结点

单链表的插入 

 修改指定位置的值

按值寻找前驱结点 

删除第一次出现的key值

删除所有的key值 

清空单链表 

完整代码 


什么是单链表?

单链表是一种链式存取的数据结构,用一组任意的存储单元存放线性表中的数据元素。链表中的数据是以结点表示的,每个结点由元素和指针组成。

在Java中我们可以将单链表定义成一个类,将单链表的基本操作定义为类中的方法,而每个结点即为类的实例化对象,每个对象中都有“元素值”和“下一个结点地址”两个属性,通过“下一个结点地址”这个属性来实现链表的链接.

单链表分为带头结点的单链表和不带头结点的单链表.

单链表虽然不能像顺序表那样实现随机存取,但单链表可以实现在任意位置插入或删除,不需要像顺序表那样需要移动大量元素才能完成,且单链表的地址是随机的,不一定是一段连续的空间.

带头结点的单链表

带头结点的单链表结构如上,每个结点的上半部分是元素值,下半部分是下一个结点的地址.

因为该链表带头结点,头结点元素值部分不存储数据,下一个结点的地址存储链表的第一个元素,在进行访问时,head.next为单链表的第一个元素.

不带头结点的单链表

不带头结点的单链表的结构如上所示,此时head即为单链表的第一个结点. 

模拟实现不带头结点的单链表

定义结点类

class Node{
    public int value;//存储结点的值
    public Node next;//用来存储下一个结点的地址

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

初始化 

public class SingleLinkedList {
    public Node head;//定义单链表的第一个结点,编译器自动初始化为null
    public int usedSize;//用来记录单链表的长度
}

头插法创建单链表 

假设单链表目前有四个元素,要在首位置插入一个结点,首先初始化一个结点node,其地址为0x123,需要令node.next = head如下图所示:

经过调整后的单链表为

  

具体代码如下: 

    public void addFirst(int data){
        Node node = new Node(data);
        if(this.head == null){
            this.head = node;
        } else {
            node.next = this.head;
            this.head = node;
        }
        this.usedSize++;
    }

尾插法创建单链表 

尾插法插入的原理其实与头插法类似,只是尾插法需要先找到链表中的最后一个结点,之后直接将新建结点放到链表最后面即可.首先Node cur = head,让其遍历单链表,找到最后一个结点,之后直接cur.next = node即完成了单链表的尾插法.

 

    //尾插法
    public void addLast(int data){
        Node node = new Node(data);
        if(this.head == null){
            this.head = node;
        } else {
            Node cur = this.head;
            while(cur.next != null){
                cur = cur.next;
            }
            cur.next = node;
        }
        this.usedSize++;
    }

打印单链表 

利用while语句遍历一遍单链表即可.

    //打印单链表
    public void myToString(){
        Node cur = this.head;
        while(cur != null){
            System.out.print(cur.value + " ");
            cur = cur.next;
        }
        System.out.println();
    }

单链表的查找

利用while语句遍历单链表,如果找到返回true,否则返回else.

    //查找是否包含关键字key是否在单链表当中
    public boolean contains(int key){
        Node cur = this.head;
        while(cur != null){
            if(cur.value == key){
                return true;
            }
            cur = cur.next;
        }
        return false;
    }

获取单链表的长度 

可以通过两种方法得到:

①直接输出usedSize;

    //得到单链表的长度
    public int length(){
        return this.usedSize;
    }

②利用while语句遍历单链表

    //得到单链表的长度
    public int length(){
        Node cur = this.head;
        int length = 0;
        while(cur != null){
            length++;
            cur = cur.next;
        }
        return length;
    }

按位置寻找前驱结点

    //寻找待插入位置的前驱结点
    public Node getIndex(int pos){
        Node cur = this.head;
        int index = 0;
        while(index != pos-1){
            cur = cur.next;
            index++;
        }
        return cur;
    }

单链表的插入 

在进行插入操作时,首先需要判断插入位置是否合法,在对特殊位置进行处理:

①如果插入位置为0,则直接利用头插法进行插入;

②如果插入位置为单链表的尾部,直接利用尾插法进行插入;

在进行正常插入时,首先需要找到待插入位置的前驱结点,之后进行插入即可.

例如要插入在第二个位置,则:

 

    //任意位置插入,第一个数据节点为0号下标
    public void insert(int pos, int data){
        if(pos < 0 || pos > this.usedSize){
            throw new RuntimeException("插入位置不合法");
        }

        if(pos == 0){
            addFirst(data);
            return;
        }

        if(pos == this.usedSize){
            addLast(data);
            return;
        }
        Node node = new Node(data);
        Node prev = getIndex(pos);
        node.next = prev.next;
        prev.next = node;
        this.usedSize++;
    }

 修改指定位置的值

利用while语句遍历找到要修改的位置,找到后利用cur.value修改即可.

    //修改指定位置的值
    public void modify(int pos, int data){
        if(head == null){
            throw new RuntimeException("链表为空,无法进行修改");
        }
        if(pos < 0 || pos > this.usedSize-1){
            throw new RuntimeException("操作位置不合法");
        }
        Node cur = this.head;
        int index = 0;
        while(index != pos){
            cur = cur.next;
            index++;
        }
        cur.value = data;
    }

按值寻找前驱结点 

    //寻找关键字key的前一个结点
    private Node getPrev(int key){
        Node prev = this.head;
        while(prev.next != null) {
            if (prev.next.value == key) {
                return prev;
            }
            prev = prev.next;
        }
        return null;
    }

删除第一次出现的key值

当要删除的key值在单链表的第一个位置时,直接head = head.next即可,如果不在第一个位置,首先需要找到该值的前驱结点,之后再进行删除操作即可.

假如待删除的key值为35,则:

 

    //删除第一次出现关键字为key的节点
    public void remove(int key){
        if(this.head == null){
            throw new RuntimeException("链表为空,无法进行操作");
        }

        if(this.head.value == key){
            //如果链表为空或第一个结点为删除的值
            this.usedSize--;
            this.head = this.head.next;
            return;
        }
        Node prev = getPrev(key);
        prev.next = prev.next.next;
        this.usedSize--;
    }

删除所有的key值 

利用循环遍历一遍单链表实现删除所有key值操作

利用定义出 prev和cur,prev存储首结点的地址

假如待删除的key值为28,则利用cur遍历一遍单链表,如果cur.value == key值,则直接利用prev.next = cur.next完成删除操作.

 

如果cur.value != key,则prev = cur; cur = cur.next; 两个都往后顺移一位,如果遇到cur.value == key这种情况,则进行第一步操作.

等到一次遍历结束后,需要再判断一下第一个结点的value值是否等于key,如果等于,则head = head.next即可.

    //删除所有值为key的节点
    public void removeAll(int key){
        if(this.head == null){
            throw new RuntimeException("单链表为空,无法进行删除");
        }
        Node prev = this.head;
        Node cur = prev.next;
        while(cur != null){
            if(cur.value == key){
                prev.next = cur.next;
                cur = cur.next;
                this.usedSize--;
            } else{
                prev = cur;
                cur = cur.next;
            }
        }
        if(this.head.value == key){
            this.head = this.head.next;
            this.usedSize--;
        }
    }

清空单链表 

直接将head结点置为null即可实现清空操作,当head结点置为null后,之后的结点对象都变成了未被引用的对象,垃圾回收器会自动回收这些未引用的对象.

    //清空单链表
    public void clear(){
        this.head = null;
    }

完整代码 

class Node{
    public int value;
    public Node next;

    public Node(int value) {
        this.value = value;
    }
}
public class SingleLinkedList {
    public Node head;//定义单链表的第一个结点,编译器自动初始化为null
    public int usedSize;//用来记录单链表的长度

    //头插法
    public void addFirst(int data){
        Node node = new Node(data);
        if(this.head == null){
            this.head = node;
        } else {
            node.next = this.head;
            this.head = node;
        }
        this.usedSize++;
    }

    //尾插法
    public void addLast(int data){
        Node node = new Node(data);
        if(this.head == null){
            this.head = node;
        } else {
            Node cur = this.head;
            while(cur.next != null){
                cur = cur.next;
            }
            cur.next = node;
        }
        this.usedSize++;
    }
    //打印单链表
    public void myToString(){
        Node cur = this.head;
        while(cur != null){
            System.out.print(cur.value + " ");
            cur = cur.next;
        }
        System.out.println();
    }
    //查找是否包含关键字key是否在单链表当中
    public boolean contains(int key){
        Node cur = this.head;
        while(cur != null){
            if(cur.value == key){
                return true;
            }
            cur = cur.next;
        }
        return false;
    }
    //得到单链表的长度
    public int length(){
        Node cur = this.head;
        int length = 0;
        while(cur != null){
            length++;
            cur = cur.next;
        }
        return length;
    }
    //寻找待插入位置的前驱结点
    public Node getIndex(int pos){
        Node cur = this.head;
        int index = 0;
        while(index != pos-1){
            cur = cur.next;
            index++;
        }
        return cur;
    }
    //任意位置插入,第一个数据节点为0号下标
    public void insert(int pos, int data){
        if(pos < 0 || pos > this.usedSize){
            throw new RuntimeException("插入位置不合法");
        }

        if(pos == 0){
            addFirst(data);
            return;
        }

        if(pos == this.usedSize){
            addLast(data);
            return;
        }
        Node node = new Node(data);
        Node prev = getIndex(pos);
        node.next = prev.next;
        prev.next = node;
        this.usedSize++;
    }
    //修改指定位置的值
    public void modify(int pos, int data){
        if(head == null){
            throw new RuntimeException("链表为空,无法进行修改");
        }
        if(pos < 0 || pos > this.usedSize-1){
            throw new RuntimeException("操作位置不合法");
        }
        Node cur = this.head;
        int index = 0;
        while(index != pos){
            cur = cur.next;
            index++;
        }
        cur.value = data;
    }

    //寻找关键字key的前一个结点
    private Node getPrev(int key){
        Node prev = this.head;
        while(prev.next != null) {
            if (prev.next.value == key) {
                return prev;
            }
            prev = prev.next;
        }
        return null;
    }

    //删除第一次出现关键字为key的节点
    public void remove(int key){
        if(this.head == null){
            throw new RuntimeException("链表为空,无法进行操作");
        }

        if(this.head.value == key){
            //如果链表为空或第一个结点为删除的值
            this.usedSize--;
            this.head = this.head.next;
            return;
        }
        Node prev = getPrev(key);
        prev.next = prev.next.next;
        this.usedSize--;
    }
    //删除所有值为key的节点
    public void removeAll(int key){
        if(this.head == null){
            throw new RuntimeException("单链表为空,无法进行删除");
        }
        Node prev = this.head;
        Node cur = prev.next;
        while(cur != null){
            if(cur.value == key){
                prev.next = cur.next;
                cur = cur.next;
                this.usedSize--;
            } else{
                prev = cur;
                cur = cur.next;
            }
        }
        if(this.head.value == key){
            this.head = this.head.next;
            this.usedSize--;
        }
    }
    //清空单链表
    public void clear(){
        this.head = null;
    }
}

 

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java实现单链表及其基本操作 的相关文章

  • ros仿真小车

    ros仿真小车 补全前面博客中缺少的一些部分关于前面博客中的robotcar 本文也可单独食用 xff09 创建工作空间并初始化 span class token function mkdir span p catkin ws src sp
  • 【2023电赛备赛】msp430f5529学习笔记(一)

    写在前 本人目前是大二在读生 xff0c 第一次参加电赛 xff0c 准备不充分 xff0c 结果熬了四天 xff0c 最后成绩却不如人意 有51和32的基础 xff0c 然后想立一个flag系统的学习一下主打超低功耗的msp430f552
  • C语言经典题:有1、2、3、4个数字,能组成多少个互不相同且无重复数字的三位数?都是多少?

    include lt stdio h gt 通过for循环将变量i j k的取值锁定在1 xff0c 2 xff0c 3 xff0c 4之间 int main int num 61 0 int i 61 0 j 61 0 k 61 0 fo
  • 单词逆序输出(c语言)

    int main int l j 61 0 int tmp 61 0 存储输入字符串的数组 char arr 100 61 34 i love beijing 34 用来存储输出字符串的数组 char arr2 100 输入字符串 gets
  • 进程虚拟地址空间

    关键词 xff1a 进程虚拟地址空间 xff0c 进程描述符 xff0c 页表 xff0c 分段式 xff0c 段页式 在进入正式的内容之前 xff0c 我们先了解一个重要的概念 进程描述符PCB 在Linux操作系统中 xff0c 描述进
  • 简单了解函数调用过程

    什么是栈帧 C C 43 43 程序的基本组成部分是函数 当程序在运行时 xff0c 每个函数每次调用都会在调用栈上维护一个独立的栈帧 xff0c 栈帧中维持着函数所需的各种信息 栈帧也叫过程活动记录 xff0c 是编译器用来实现过程 函数
  • 错题汇总1

    1 以下程序的运行结果是 xff08 xff09 int main void printf 34 s 5 3s n 34 34 computer 34 34 computer 34 return 0 A computer puter B c
  • 使用C/C++制作信息管理系统(demo)

    要求 xff1a 在windows环境下使用Vistual studio以C C 43 43 语言编译一个具有基础框架的客户信息管理系统 必须使用到封装 继承 map容器 SQL数据库技术 我 是 分 割 线 未经过UI处理的基础系统功能效
  • 错题汇总2

    1 下列程序的打印结果是 char p1 15 61 34 abcd 34 p2 61 34 ABCD 34 str 50 61 34 xyz 34 strcpy str 43 2 strcat p1 43 2 p2 43 1 printf
  • C++之继承初识(不包含虚拟继承)

    C 43 43 是一种面向对象的语言 xff0c 而面向对象 xff0c 有着三大特征 封装 xff0c 继承 xff0c 多态 关于封装 xff0c 在我的其它博客中已经有过简单的介绍了 这里我将简单叙述一下面向对象的三大特征之二 继承
  • C++之虚拟继承与继承的小总结

    本来是想将虚拟继承的部分写在上一篇的 xff0c 但是虚拟继承的分析实在有些复杂 xff0c 为了方便我自己回顾 xff0c 就干脆单写一篇吧 我们之前说过了 xff0c 虚拟继承可以解决菱形继承的二义性以及数据冗余的问题 xff0c 实际
  • C++之初识多态(Visual Studio 2019)

    此文章关于多态的代码全部是使用Visua Studio2019 x86 实现的 xff0c C 43 43 多态在不同编译器中的实现细节可能不同 xff0c 所以部分情况下相同代码运行结果可能不同 xff0c 在此声明 目录 多态的概念 多
  • C工程与寄存器封装(lv9-day13)

    文章目录 C工程与寄存器封装1 C语言工程简介2 启动文件3 C语言实现LED闪烁3 1 C语言与汇编分别是怎么操作寄存器的3 2 用C语言实现LED闪烁 4 寄存器的封装4 1 第一种封装方式 xff08 宏定义 xff09 4 2 第二
  • C++中auto的用法

    目录 1 auto初始化 1 1auto还可以与指针 xff0c 引用以及const进行组合 xff0c 但是在不同场景下会有相对应的推导规则 1 1 1指针和引用 1 1 2const 2 增强for循环 3 auto的限制 C 43 4
  • 自定义函数实现strcat函数功能

    strcat函数定义 字符串追加 连接函数 它的功能就是在一个字符串后面追加上另外一个字符串 char strcat char destination const char source 特点 xff08 与strcpy类似 xff09 x
  • STM32实现智能加湿

    开发前的准备需要如下的材料 xff1a 雾化模块1个 STM32F103开发板一个 风扇驱动模块1个 xff08 可用继电器代替 xff09 我们采用的继电器是低电平触发的所以我们在使用的时候只用给它一个低电平的信号就可以控制它了 USB转
  • Qt项目实战:愤怒的小鸟(联机版)

    前言 本文章会详细介绍难点的内容 xff0c 不附带全部源码 xff0c 会将关键代码进行展示 因为只有截图 xff0c 这里在每一个动作和界面都添加了音效与BGM 同时附加了CG展示 素材和音效全部放在下面了 xff0c 需要可自行提取
  • Ubuntu中安装openCV时Cmake问题解决

    1 执行Cmake的语句指令 sudo cmake D CMAKE BUILD TYPE 61 Release D CMAKE INSTALL PREFIX 61 usr local 2 当执行完上述指令后遇见以下问题解决策略 问题1 xf
  • 使用 RGB-D 相机(Astra)实现 YOLO v3 实时目标检测

    设备和环境 xff1a 奥比中光RGB D相机 xff08 Astra xff09 xff1b Ubuntu16 04 首先 xff0c 先将自己的RGB D相机的环境与依赖构建好 xff0c 然后进行以下步骤构建darknet ros 1
  • Arduion应用U8g2库实现字符滚动效果

    由于U8g2库中没有可以位移的函数 xff0c 所以简单编写了一个可以实现字符滚动的代码 主要是为了记录一下自己学习Arduion的过程 算是一个记事本吧 xff01 当然如果你对于这方面有所需求 xff0c 可以拿去使用 主要是利用显示器

随机推荐

  • C语言老鼠走迷宫(单路径)算法详细讲解

    最近在学习C语言的一些经典算法 xff0c 其中遇到了一点困难 xff0c 导致卡进度了 琢磨了很久 xff0c 在绘制流程图时 xff0c 突然灵感大开理解了 xff0c 老鼠走迷宫算法的奇妙 所以写了这个 xff0c 一来是方便以后右和
  • 关于二分搜索法条件判断的研究

    最近在写二分搜索法时 xff0c 发现了几个现象 xff0c 在不同的条件设定之下 xff0c 系统会出现几种bug xff1a 1 当搜索的值 lt 最小值 时系统都会一直在循环内执行 2 当搜索的值 gt 最大值 时系统都会一直在循环内
  • C++ 库函数<string>示例

    最近在学C 43 43 自己整理了一部分C 43 43 库函数 lt string gt 的一些 函数 主要都是C 43 43 的 想到以后可能会用到 所以打算记录一下 方便自己也方便了大家 在啃这些函数的时候有很多借鉴之处 xff0c 在
  • 两台电脑间的串口通信

    目录 一 准备工作 二 实验过程 三 实验结果 一 准备工作 1 两台笔记本电脑 2 2个usb转串口模块 3 杜邦线若干 4 秒表 二 实验过程 两个串口线分别连接两台电脑 连线方式 xff1a 3V3 3V3 xff0c GND GND
  • c++ primer plus学习笔记(1)——基础知识

    本人还有一星期要开始期末考试了 xff0c 复习c 43 43 时顺便挖个坑 xff0c 之后会详细更新 目录 1 初识源代码 1 1 c 43 43 程序的产生 1 2 代码例 1 3 标记 空白 2 简单数据类型 2 1 变量名 2 2
  • C语言:sizeof和strlen计算字符串大小

    大家清楚 sizeof 和 strlen 的区别吗 xff1f sizeof是运算符 xff0c 确定的是字符所占空间大小 xff0c 参数可以有数组 指针 类型 对象 函数等 strlen是C语言的标准库函数 xff0c 确定是字符串的大
  • Python--爬虫--requests进阶,cookie/session模拟登录

    目录 一 原理 二 实际操作 三 结果 四 问题与总结 一 原理 以下内容为使用requests库发送请求 xff0c 使用cookie session模拟登录 xff08 并且登录时只需输入账号与密码 xff09 我们在使用搜索引擎访问网
  • linux系统移植U-boot与kernel的搭载流程(交互模式下)

    Linux系统移植四大部分 xff1a 搭建交叉开发环境 bootloader的选择和移植 本文选用bootloader下的U boot uImage的配置 编译与移植 根文件系统的制作 全部已完成 xff0c 本文只讲解 如何搭载这些东西
  • FreeRTOS任务创建、删除| FreeRTOS三

    目录 一 FreeRTOS任务创建与删除有关函数 1 1 创建 删除任务的API函数 1 1 1 动态创建任务 1 1 2 静态创建任务 1 1 3 删除任务 二 FreeRTOS任务创建与删除 xff08 动态方法 xff09 2 1 实
  • (学习)基于STM32的串口通信打印数据(HAL库+CubeMX)

    当我们在进行开发的时候 xff0c 通常需要将一些参数进行调整来达到效果 xff0c 这个时候就需要将单片机上的信息通过串口通信传送到PC机上来直观显示 一 基本的专有名词和原理介绍 USART xff1a 只能异步通信 USART xff
  • ROS通信——C++实现

    一 普通话题通信 创建功能包 xff1a catkin create pkg package roscpp rospy std msgs 创建发布者 xff1a include 34 ros ros h 34 include 34 std
  • ROS使用Python编写的步骤

    第一步 xff1a 和C 43 43 编写一样 xff0c 配置好工作空间 第二步 xff1a 在功能包下面建立一个scripts文件夹 第三步 xff1a 在scripts文件里面建立一个 py文件 第四步 编写python文件 注意 x
  • window的QT作为TCP客户端,ubuntu的python作为TCP服务端

    客户端 pro文件加入 QT 43 61 network mainwindow h ifndef MAINWINDOW H define MAINWINDOW H include lt QMainWindow gt include lt Q
  • RoboCom机器人大赛使用yolov5抽取20个随机图片进行人群识别

    目录 1 原理 2 思维流程 2 1 进行yolov5的环境搭建 2 1 1 在Linux的ubuntu环境anaconda的安装 2 1 2 Vscode的安装和配置 2 1 3 Github上面yolov5文件的下载 2 1 4 使用A
  • C语言实现字符串逆序、倒置字符串(字符串逆序问题的升级)

    一 字符串逆序 问题描述 xff1a 输入一个字符串str xff0c 将其内容颠倒过来 xff0c 并输出 数据范围0 lt len str lt 10000 输入描述 xff1a 输入一个字符串 xff0c 可以有空格 输出描述 xff
  • C语言实现“井字棋”游戏(三子棋)人机对弈

    井字棋游戏 xff1a 即三子棋 xff0c 英文名叫Tic Tac Tic xff0c 是一种在3 3格子上进行的连珠游戏 xff0c 和五子棋比较类似 xff0c 由于棋盘一般不画边线框 xff0c 格线排成井字故得名 题目分析 xff
  • C语言基础编程练习(精选例题+题解)

    目录 1 求最大公约数和最小公倍数 2 打印图形 3 质数因子 4 数字排序 5 十进制数转换为八进制数 xff08 进制转换 xff09 6 寻找完数 1 求最大公约数和最小公倍数 题目描述 xff1a 输入两个正整数m和n xff0c
  • C语言——实现“扫雷”小游戏(简易版)

    扫雷游戏想必我们都有玩过 xff0c 那么今天就用C语言来简单实现 扫雷 小游戏 xff01 目录 一 游戏规则 二 基本思路介绍 三 各功能代码实现 1 创建用户交互界面 2 初始化棋盘函数 3 埋雷函数 4 显示棋盘函数 5 排雷函数
  • linux驱动开发关于内核模块、设备模型、内存管理、模块参数介绍

    内核功能 多任务管理 内存管理 文件管理 网络协议栈 设备管理 设备驱动 我是分割线 一 内核开发特点 内核代码的运行时机 启动目标硬件应用程序调用系统调用硬件中断调用中断处理Linux内核和驱动间的接口 kABI 不稳定内核升级后 接口可
  • Java实现单链表及其基本操作

    目录 什么是单链表 xff1f 带头结点的单链表 不带头结点的单链表 模拟实现不带头结点的单链表 定义结点类 初始化 头插法创建单链表 尾插法创建单链表 打印单链表 单链表的查找 获取单链表的长度 按位置寻找前驱结点 单链表的插入 修改指定