Java实现一个带头节点的单链表

news2024/12/14 14:53:44
什么是单链表?

单链表是一种基础的数据结构,其中每个节点都包含两部分:

  1. 数据域:存储节点数据。
  2. 指针域:存储指向下一个节点的引用。

为什么使用头节点?

头节点的存在简化了操作逻辑:

  • 统一操作逻辑:即使链表为空,头节点也存在,从而避免特殊情况的判断。

  • 简化插入和删除:无需特殊处理第一个节点的操作。

  • 没有头节点的add:你必须对是头节点插入进行处理,把当前的node给头节点,这样才不是null,后续才能进行正常查找最后一个的node节点然后进行指向。详情查看下面代码演示过程。

package com.algorithm.dev.linked;

import lombok.AllArgsConstructor;
import lombok.Data;


/**
 * @author xiazhihao
 * @ClassName: NoHeadSingleLinkedList
 * @ClassNameExplain:
 * @Description: xiazhihao
 * @date 2024/12/13 
 */
public class NoHeadSingleLinkedList {

    /**
     * 起始位置 没有设置头节点 现在它为null
     */
    private Node head;

    /**
     * 添加数据
     * @param data 待添加的数据
     */
    public void add(Object data){
        Node newNode = new Node(data, null);
        //没有头节点需要判断 因为必须告知起始的地址
        //【】
        if (null == head){
            //【node|next】->
            head = newNode;
        }
        //有新的话必须 往后面找
        //【1|next】—> 【1|next】 -> null
        //假设你不判断null == head 那么没有头节点插入就会空指针
        else {
            //当前处理的node节点 后面为了找到最后一个会不断遍历更新
            Node currentNode = newNode;

            while ( null != currentNode.next){
                //没找到一直更新当前遍历的情况
                currentNode = currentNode.next;
            }
            //找到了就证明找到了结尾 直接更改指向就链上了

            currentNode.next = newNode;

        }
    }

    @Data
    @AllArgsConstructor
     class Node{

        /**
         * 数据域
         */
        private Object data;

        /**
         * 下一个指针
         */
        private Node next;

     }

}
  • 有头节点的add:不管是不是头节点都可以直接按一套逻辑查找,直接找最后的node,因为头节点给了入口进行查找,不会出现null的情况。
/**
 * @author xiazhihao
 * @ClassName: SingleLinkedList
 * @ClassNameExplain:
 * @Description: 有头节点的标志单链表
 * @date 2024/12/13 
 */
@ToString
public class SingleLinkedList {

    /**
     * 头节点
     */
    private Node head = new Node("我是头节点,不要动我,我是多余的,我为方便新增或者删除少做逻辑判断",null);


    /**
     * 新增链表数据 尾插o(n)
     * @param data 数据
     */
    public void add(Object data){
        Node newNode = new Node(data,null);
        //用于后续编辑找到最后一个节点 代表当前遍历的位置
        Node currentNode = head;

        while (null != currentNode.next){
            currentNode = currentNode.next;
        }
        //【currentNode|next】 -> 【newNode|next】 -> null
        //找到了最后的节点
        currentNode.next = newNode;
    }
}

链表实现及方法解析

链表结构

初始状态下链表只有一个头节点:

【head|next】-> null

1. 新增节点:尾插法

代码实现:

/**
 * 新增链表数据 尾插o(n)
 * @param data 数据
 */
public void add(Object data){
    Node newNode = new Node(data,null);
    // 用于后续编辑找到最后一个节点,代表当前遍历的位置
    Node currentNode = head;

    while (null != currentNode.next){
        currentNode = currentNode.next;
    }
    //【currentNode|next】 -> 【newNode|next】 -> null
    // 找到了最后的节点
    currentNode.next = newNode;
}

操作示意图:

  1. 插入 “1” 后:
    【head|next】-> 【1|next】-> null
    
  2. 插入 “2” 后:
    【head|next】-> 【1|next】-> 【2|next】-> null
    

2. 新增节点:头插法

代码实现:

/**
 * 头插法 o(1)
 * @param data
 */
public void afterAdd(Object data){
    Node newNode = new Node(data,null);
    // 【1|next】-> null
    newNode.next = head;
    head = newNode;
    // 【newNode|next】->【1|next】-> null
}

操作示意图:

  1. 插入 “2” 后:

    【head|next】-> 【2|next】-> null
    
  2. 插入 “3” 后:

    【head|next】-> 【3|next】-> 【2|next】-> null
    

3. 查找节点

代码实现:

/**
 * 查找出指定节点
 */
public Node find(Object data){
    // 头节点不需要查找
    Node currentNode = head.next;
    if (null != currentNode){
        // 一直往下找
        while (null != currentNode.next){
            if (currentNode.data.equals(data)){
                return currentNode;
            }
            // 继续往下滚
            currentNode = currentNode.next;
        }
        return null;
    }
    // 啥都没有
    return null;
}

操作示意图:
查找 “2” 的节点:

【head|next】-> 【1|next】-> 【2|next】-> null
                   ↑
                  查找

4. 删除节点:按数据删除

代码实现:

/**
 * 删除节点
 * @param data 待删除的节点
 * @return true 删除成功 false 删除失败
 */
public boolean remove(Object data){
    if (isEmpty()){
        return false;
    }
    Node currentNode = head;

    while (null != currentNode.next){
        // 找当前系节点的下一个数据是符合删除的
        if (currentNode.next.data.equals(data)){
            // 找到了
            currentNode.next = currentNode.next.next;
            // 后续会自动释放2
            return true;
        }
        // 继续往下滚
        currentNode = currentNode.next;
    }
    return false;
}

5. 删除节点:按索引删除

代码实现:

/**
 * 删除指定坐标node
 * @param index 坐标
 * @return true 删除成功 false 删除失败
 */
public boolean remove(int index){
    // 前驱节点
    Node preNode = head;
    // 遍历到指定位置的前驱节点
    for (int i = 0; i < index; i++) {
        if (preNode.next == null) {
            return false; // 索引超出范围
        }
        preNode = preNode.next;
    }
    // 删除了前驱next的节点
    if (preNode.next != null) {
        preNode.next = preNode.next.next; // 前驱节点指向后继节点
        return true;
    }
    return false;
}

6. 获取节点:按索引获取

代码实现:

/**
 * 获取node节点
 * @param index 坐标从0开始
 * @return
 */
public Node get(int index){
    Node currentNode = head;
    for (int i = 0; i <= index; i++) {
        if (null == currentNode.next){
            return null; // 超界
        }
        currentNode = currentNode.next;
    }
    return currentNode;
}

测试代码与运行结果

测试代码:

public static void main(String[] args) {
    SingleLinkedList singleLinkedList = new SingleLinkedList();
    singleLinkedList.add("1");
    singleLinkedList.add("2");
    singleLinkedList.add("3");

    // 删除索引3的节点
    boolean remove = singleLinkedList.remove(3);
    System.out.println(remove); // 输出 false

    // 输出链表
    System.out.println(singleLinkedList);
}

运行结果:

false
SingleLinkedList(head=Node(data=我是头节点,不要动我,我是多余的,我为方便新增或者删除少做逻辑判断, next=Node(data=1, next=Node(data=2, next=Node(data=3, next=null)))))

完整代码

以下是完整代码:

package com.algorithm.dev.linked;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.ToString;

import java.util.LinkedList;

/**
 * @author xiazhihao
 * @ClassName: SingleLinkedList
 * @ClassNameExplain:
 * @Description: 有头节点的标志单链表
 * @date 2024/12/13
 */
@ToString
public class SingleLinkedList {

    /**
     * 头节点
     */
    private Node head = new Node("我是头节点,不要动我,我是多余的,我为方便新增或者删除少做逻辑判断",null);


    /**
     * 新增链表数据 尾插o(n)
     * @param data 数据
     */
    public void add(Object data){
        Node newNode = new Node(data,null);
        //用于后续编辑找到最后一个节点 代表当前遍历的位置
        Node currentNode = head;

        while (null != currentNode.next){
            currentNode = currentNode.next;
        }
        //【currentNode|next】 -> 【newNode|next】 -> null
        //找到了最后的节点
        currentNode.next = newNode;
    }

    /**
     * 头插法 o(1)
     * @param data
     */
    public void afterAdd(Object data){
        Node newNode = new Node(data,null);
        //【1|next】-> null
        newNode.next = head;
        head = newNode;
        //【newNode|next】->【1|next】-> null
    }

    /**
     * 查找出指定节点
     */
    public Node find(Object data){
        //头节点不需要查找
       Node currentNode = head.next;
       if (null != currentNode){
           //一直往下找
           while ( null != currentNode.next){
               if (currentNode.data.equals(data)){
                   return currentNode;
               }
               //继续往下滚
               currentNode = currentNode.next;
           }
           return null;
       }
       //啥都没有
       return null;
    }

    /**
     *
     */
    public boolean isEmpty(){
        return head.next == null;
    }

    /**
     * 删除节点
     * @param data 待删除的节点
     * @return true 删除成功 false 删除失败
     */
    public boolean remove(Object data){
        if (isEmpty()){
            return false;
        }
        Node currentNode = head;

        //【1|next】->【2|next】->【3|next】->
        //【1|next】->【3|next】->

        //
        while ( null != currentNode.next){
            //找当当前系节点的下一个数据是符合删除的代表需要上面所属的操作 1链入3
            if (currentNode.next.data.equals(data)){
                //找到了
                currentNode.next = currentNode.next.next;
                //后续会自动释放2
                return true;
            }
            //继续往下滚
            currentNode = currentNode.next;
        }
        return false;
    }

    /**
     * 删除指定坐标node
     * @param index 坐标
     * @return true 删除成功 false 删除失败
     */
    public boolean remove(int index){
        //前驱节点
        Node preNode = head;
        // 遍历到指定位置的前驱节点
        for (int i = 0; i < index; i++) {
            if (preNode.next == null) {
                return false; // 索引超出范围
            }
            preNode = preNode.next;
        }
        //删除了前驱next的节点 自动释放 如果不是最后一个
        if (preNode.next != null) {
            preNode.next = preNode.next.next; // 前驱节点指向后继节点
            return true;
        }
        return false;
    }

    /**
     * 获取node节点
     * @param index 坐标从0开始
     * @return
     */
    public Node get(int index){
        Node currentNode = head;
        for (int i = 0; i <= index; i++) {
            //下一个坐标没有 还在遍历肯定是超界了
           if ( null == currentNode.next){
               return null;
            }
           currentNode = currentNode.next;
        }
        return currentNode;
    }



    @AllArgsConstructor
    @Data
    class Node{

        /**
         * 数据
         */
        private Object data;

        /**
         * next指针
         */
        private Node next;
    }

    public static void main(String[] args) {
        SingleLinkedList singleLinkedList = new SingleLinkedList();
        singleLinkedList.add("1");
        singleLinkedList.add("2");
        singleLinkedList.add("3");
        boolean remove = singleLinkedList.remove(3);
        System.out.println(remove);
        System.out.println(singleLinkedList);
    }

}

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

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

相关文章

ORACLE 导入导出数据库(包含表结构和数据)

导出 1、进入本地oracle 驱动安装目录下–> 进入CMD 2、输入命令 exp 用户名/密码10.xx.xx.xx:1521/orcl fileexport.dmp 3、查看导出的文件 导入 1、进入本地oracle 驱动安装目录下–> 进入CMD 2、输入命令 imp 用户名/密码10.xx.xx.xx:1521/orcl fully ignorey…

Qt之将源代码封装成库文件使用(五)

Qt开发 系列文章 - Code-To-Library&#xff08;五&#xff09; 目录 前言 一、库文件 二、直接封装方式 1.静态库封装 2.动态库封装 3.其它库类型 三、二次重写封装 四、库的使用 1.移植库及头文件 2.添加外部库 总结 前言 库文件是计算机上的一类文件&#xff0c…

视频监控汇聚平台方案设计:Liveweb视频智能监管系统方案技术特点与应用

随着科技的发展&#xff0c;视频监控平台在各个领域的应用越来越广泛。然而&#xff0c;当前的视频监控平台仍存在一些问题&#xff0c;如视频质量不高、监控范围有限、智能化程度不够等。这些问题不仅影响了监控效果&#xff0c;也制约了视频监控平台的发展。 为了解决这些问…

跨平台开发技术的探索:从 JavaScript 到 Flutter

随着多平台支持和用户体验一致性在应用程序开发中变得越来越重要,开发者面临的挑战是如何在不同平台上保持代码的可维护性和高效性。本文将探讨如何利用现代技术栈,包括 Flutter、JavaScript、HTML5、WebAssembly、TypeScript 和 Svelte,在统一的平台上进行高效的跨平台开发…

Dcoker安装nginx,完成反向代理和负载均衡

1. 简介 官网&#xff1a;nginx Nginx是一个高性能的 HTTP 和反向代理 Web 服务器。它的主要功能包括反向代理、负载均衡和动静分离等。正因为 Nginx的这些功能能够为系统带来性能和安全方面的诸多优势&#xff0c;我们在项目部署时需要引入 Nginx组件。接下来我们会逐一向大…

Allegro X PCB设计小诀窍--如何在Allegro X中进行PCB设计评审

背景介绍&#xff1a;在PCB设计过程中&#xff0c;企业为了提升PCB设计质量&#xff0c;确保PCB设计的可制造性&#xff0c;缩短产品的研发周期&#xff0c;通常需要组织对PCB进行设计评审。但是目前的PCB设计评审过程存在评审文档管理繁琐、意见反馈不及时、问题传递不准确、评…

基于多视角深度学习技术的乳腺X线分类:图神经网络与Transformer架构的研究|文献速递-生成式模型与transformer在医学影像中的应用速递

Title 题目 Mammography classification with multi-view deep learning techniques:Investigating graph and transformer-based architectures 基于多视角深度学习技术的乳腺X线分类&#xff1a;图神经网络与Transformer架构的研究 01 文献速递介绍 乳腺X线检查是乳腺癌…

HCIA-Access V2.5_2_3_网络通信基础_以太网概述

什么是以太网 以太网是由IEEE定义的局域网技术&#xff0c;也是目前应用最普遍的技术&#xff0c;早期的令牌环网&#xff0c;FDDI等局域网技术都被它取代了&#xff0c;以太网主要分为两类&#xff0c;共享型以太网和交换型以太网。共享式以太网主要采用总线型的拓扑结构&…

Maven学习(Maven项目模块化。模块间“继承“机制。父(工程),子项目(模块)间聚合)

目录 一、Maven项目模块化&#xff1f; &#xff08;1&#xff09;基本介绍。 &#xff08;2&#xff09;汽车模块化生产再聚合组装。 &#xff08;3&#xff09;Maven项目模块化图解。 1、maven_parent。 2、maven_pojo。 3、maven_dao。 4、maven_service。 5、maven_web。 6…

Leecode刷题C语言之K次乘法运算后的最终数组①

执行结果:通过 执行用时和内存消耗如下&#xff1a; 代码如下&#xff1a; int* getFinalState(int* nums, int numsSize, int k, int multiplier, int* returnSize) {int *ret (int *)malloc(sizeof(int) * numsSize);memcpy(ret, nums, sizeof(int) * numsSize);while (k…

Source Insight 4.0的安装

一、安装与破解 1、下载Source Insight 4.0安装包 https://pan.baidu.com/s/1t0u1RM19am0lyzhlNTqK9Q?pwdnvmk 2、下载程序破解补丁包 https://pan.baidu.com/s/1irvH-Kfwjf4zCCtWJByqJQ 其中包含文件si4.pediy.lic 和 sourceinsight4.exe。 3、安装下载的Source Insight …

UNIX数据恢复—UNIX系统常见故障问题和数据恢复方案

UNIX系统常见故障表现&#xff1a; 1、存储结构出错&#xff1b; 2、数据删除&#xff1b; 3、文件系统格式化&#xff1b; 4、其他原因数据丢失。 UNIX系统常见故障解决方案&#xff1a; 1、检测UNIX系统故障涉及的设备是否存在硬件故障&#xff0c;如果存在硬件故障&#xf…

重生之我在异世界学编程之C语言:深入文件操作篇(上)

大家好&#xff0c;这里是小编的博客频道 小编的博客&#xff1a;就爱学编程 很高兴在CSDN这个大家庭与大家相识&#xff0c;希望能在这里与大家共同进步&#xff0c;共同收获更好的自己&#xff01;&#xff01;&#xff01; 函数递归与迭代 引言正文一、为什么要用文件二、文…

内网是如何访问到互联网的(华为源NAT)

私网地址如何能够访问到公网的&#xff1f; 在上一篇中&#xff0c;我们用任意一个内网的终端都能访问到百度的服务器&#xff0c;但是这是我们在互联网设备上面做了回程路由才实现的&#xff0c;在实际中&#xff0c;之前也说过运营商是不会写任何路由过来的&#xff0c;那对于…

VSCode 报错:rust-analyzer requires glibc >= 2.28 in latest build

报错信息 /home/jake/.vscode-server-insiders/extensions/matklad.rust-analyzer-0.3.953/server/rust-analyzer: /lib/x86_64-linux-gnu/libc.so.6: version GLIBC_2.29 not found (required by /home/jake/.vscode-server-insiders/extensions/matklad.rust-analyzer-0.3.9…

软考:工作后再考的性价比分析

引言 在当今的就业市场中&#xff0c;软考&#xff08;软件设计师、系统分析师等资格考试&#xff09;是否值得在校学生花费时间和精力去准备&#xff1f;本文将从多个角度深入分析软考在不同阶段的性价比&#xff0c;帮助大家做出明智的选择。 一、软考的价值与局限性 1.1 …

Hadoop一课一得

Hadoop作为大数据时代的奠基技术之一&#xff0c;自问世以来就深刻改变了海量数据存储与处理的方式。本文将带您深入了解Hadoop&#xff0c;从其起源、核心架构、关键组件&#xff0c;到典型应用场景&#xff0c;并结合代码示例和图示&#xff0c;帮助您更好地掌握Hadoop的实战…

HTML综合

一.HTML的初始结构 <!DOCTYPE html> <html lang"en"><head><!-- 设置文本字符 --><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><!-- 设置网页…

c#笔记2024

Ctrl r e自动添加get和set CompositeCurve3d 复合曲线 List<Entity> entS listline.Cast<Entity>().ToList();//list类型强转 前面拼上\u0003&#xff0c;就可以实现&#xff0c;不管有没有命令都能打断当前命令的效果 取消其他命令&#xff1a;Z.doc.SendStri…

debian12学习笔记

前置条件 基于debian12官网的qcow2格式文件进行操作 安装ssh 登录虚拟机后安装ssh服务端 apt install openssh-server配置国内源 新增/etc/apt/sources.list.d/tsinghua.list 使用清华大学的源 https://www.cnblogs.com/shanhubei/p/18104430 deb https://mirrors.tuna.t…