Java实现一个带头节点的单链表
什么是单链表?
单链表是一种基础的数据结构,其中每个节点都包含两部分:
- 数据域:存储节点数据。
- 指针域:存储指向下一个节点的引用。
为什么使用头节点?
头节点的存在简化了操作逻辑:
-
统一操作逻辑:即使链表为空,头节点也存在,从而避免特殊情况的判断。
-
简化插入和删除:无需特殊处理第一个节点的操作。
-
没有头节点的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@AllArgsConstructorclass 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” 后:
【head|next】-> 【1|next】-> null - 插入 “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】-> nullnewNode.next = head;head = newNode;// 【newNode|next】->【1|next】-> null
}
操作示意图:
-
插入 “2” 后:
【head|next】-> 【2|next】-> null -
插入 “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;// 后续会自动释放2return 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】-> nullnewNode.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链入3if (currentNode.next.data.equals(data)){//找到了currentNode.next = currentNode.next.next;//后续会自动释放2return 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@Dataclass 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);}}相关文章:
Java实现一个带头节点的单链表
什么是单链表? 单链表是一种基础的数据结构,其中每个节点都包含两部分: 数据域:存储节点数据。指针域:存储指向下一个节点的引用。 为什么使用头节点? 头节点的存在简化了操作逻辑: 统一操作…...
【图像配准】方法总结
图像配准(Image registration)就是将不同时间、不同传感器(成像设备)或不同条件下(天候、照度、摄像位置和角度等)获取的两幅或多幅图像进行匹配、叠加的过程,就是找到1幅图像像素到另1幅图像像素间的空间映射关系它已…...
LabVIEW汽车综合参数测量
系统基于LabVIEW虚拟仪器技术,专为汽车带轮生产中的质量控制而设计,自动化测量和检测带轮的关键参数。系统采用PCIe-6320数据采集卡与精密传感器结合,能够对带轮的直径、厚度等多个参数进行高精度测量,并通过比较测量法判定产品合…...
三相异步电动机没有气压怎么办?
三相异步电动机作为工业和商业应用中最常见的电动机类型之一,广泛应用于各类机械设备及自动化系统中。其运行依赖于电能的转换,然而在某些情况下,可能会出现电动机驱动设备无法获得气压的情况。 一、三相异步电动机工作原理 三相异步电动机…...
软件工程书籍推荐
软件工程推荐这几本书: 1、软件设计的哲学(第2版) 本书深入探讨了软件设计中的核心问题:如何将复杂的软件系统分 解为可以相对独立实现的模块(例如类和方法),从而降低其复杂性并 提高开发效率。…...
验证集和测试集的区别
验证集(Validation Set)和测试集(Test Set)在机器学习模型训练过程中扮演着不同的角色,以下是它们之间的主要区别: 目的: 验证集:用于在模型训练过程中调整模型的超参数和做出训练…...
OpenIPC开源FPV之Adaptive-Link天空端代码解析
OpenIPC开源FPV之Adaptive-Link天空端代码解析 1. 源由2. 框架代码3. 报文处理3.1 special报文3.2 普通报文 4. 工作流程4.1 Profile 竞选4.2 Profile 研判4.3 Profile 应用 5. 总结6. 参考资料7. 补充资料7.1 RSSI 和 SNR 的物理含义7.2 信号质量加权的理论依据7.3 实际应用中…...
Next.js流量教程:核心 Web Vitals的改善
更多有关Next.js教程,请查阅: 【目录】Next.js 独立开发系列教程-CSDN博客 目录 引言 1. 什么是 Core Web Vitals? 1.1 Largest Contentful Paint (LCP) 1.2 First Input Delay (FID) 1.3 Cumulative Layout Shift (CLS) 2. 如何优化 …...
百度智能云千帆AppBuilder升级,百度AI搜索组件上线,RAG支持无限容量向量存储!
百度智能云千帆 AppBuilder 发版升级! 进一步降低开发门槛,落地大模型到应用的最后一公里。在千帆 AppBuilder 最新升级的 V1.1版本中,企业级 RAG 和 Agent 能力再度提升,同时组件生态与应用集成分发更加优化。 • 企业级 RAG&am…...
构建树莓派温湿度监测系统:从硬件到软件的完整指南
✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏…...
12.11数据结构-图
无向完全图:在无向图中,如果任意两个顶点之间都存在边,则称该图为无向完全图。 有向完全图:在有向图中,如果任意两个顶点之间都存在方向相反的两条弧,则称该图为有向完全图。 含有n个顶点的无向完全图有…...
BERT模型入门(2)BERT的工作原理
文章目录 如名称所示,BERT(来自Transformer的双向编码器表示)是基于Transformer模型。我们可以将BERT视为只有编码器部分的Transformer。 在上一个主题《Transformer入门》中,我们了解到将句子作为输入喂给Transformer的编码器&a…...
python3 中的成员运算符
一. 简介 在Python 3中,成员运算符用于测试序列(如字符串、列表、元组、集合或字典)中是否包含某个值。身份运算符用于比较两个对象的身份,即它们是否引用内存中的同一个对象。 本文简单学习一下 python3 中的成员运算符与身份运…...
【测试面试篇1】测试开发与开发|selenium实现自动化测试|设计测试用例|常见的测试方法|开发不认可提测试的bug该怎么办
目录 1.选择走测试为什么还要学这么多的开发知识? 2.为什么选择软件测试开发岗位而不是软件开发岗位? 3.个人的职业规划是什么? 4.测试中遇到的问题如何进行解决? 5.对自己的项目做过哪些测试工作? 6.描述selenium…...
人大金仓数据linux安装注意事项
人大金仓数据linux安装注意事项 本次是个人搭建虚拟机安装centos7的环境下进行安装。 1、安装流程参照https://help.kingbase.com.cn/v9/install-updata/install-linux/preface.html。 2、mount安装文件报错 操作手册提供mount的命令如下: mount KingbaseES_V009R0…...
【Maven】多模块项目的构建
项目构建 什么是构建? 项目构建指的是将源代码和资源文件转换为可执行或可分发的软件制品(如 JAR、WAR 文件)的过程。这个过程不仅包括编译代码,还包括运行测试、打包、部署等步骤。Maven 提供了一套标准化的方法来处理这些任务…...
大模型学习笔记------SAM模型详解与思考
大模型学习笔记------SAM模型详解与思考 1、SAM框架概述2、Segment Anything Task3、Segment Anything Model SAM模型是Meta 提出的分割一切模型(Segment Anything Model,SAM)突破了分割界限,极大地促进了计算机视觉基础模型的发展…...
crictl和ctr与docker的命令的对比
crictl是遵循CRI接口规范的一个命令行工具,通常用它来检查和管理kubelet节点上的容器运行时和镜像 ctr是containerd的一个客户端工具, 接下来就是crictl的的常见命令,其中能完全替代docker命令的参照下列表格 操作crictldocker查看运行容器…...
SQLite建表语句示例(含所有数据类型、索引、自增主键、唯一索引)
下面是一个示例,展示如何创建一个用户信息表。 包含 SQLite 支持的所有数据类型,同时设置主键为自增、一个字段为唯一索引,以及另一个字段为普通索引: -- 创建用户信息表 CREATE TABLE user_info (id INTEGER PRIMARY KEY AUTOI…...
探秘Redis哨兵模式:原理、运行与风险全解析
一、引言 Redis 概述 在当今的数据存储领域,Redis 占据着十分重要的地位。它是一个内存中的数据存储,凭借其出色的性能和丰富的功能,被数百万开发人员广泛应用于诸多场景之中,已然成为构建高性能、可扩展应用程序的得力工具。 从…...
如何使用4个经过验证的技巧将Android联系人备份到Mac
联系人无疑是我们智能手机上最重要的数据。一旦失去联系,我们就会与这个世界上最亲爱的人失去联系;也许他们是家人、爱人、朋友、同学、同事、学生等。因此,联系人备份对我们来说非常重要。与将iPhone联系人备份到Mac相对容易不同,…...
WzComparerR2:解锁冒险岛游戏数据洞察的专业工具
WzComparerR2:解锁冒险岛游戏数据洞察的专业工具 【免费下载链接】WzComparerR2 Maplestory online Extractor 项目地址: https://gitcode.com/gh_mirrors/wz/WzComparerR2 想要深入探索《冒险岛》这款经典游戏的数据世界吗?WzComparerR2是您的专…...
如何用UAV-Flow实现语音控制无人机?手把手教你搭建环境与避坑指南
如何用UAV-Flow实现语音控制无人机?从环境搭建到实战避坑全指南 当无人机遇上自然语言处理,会擦出怎样的火花?去年接触UAV-Flow时,我正为一个农业巡检项目头疼——传统摇杆控制需要专业飞手,而农户们更习惯说"绕着…...
C语言回调函数在TCP客户端中的应用与实践
1. 回调函数基础概念解析回调函数是C语言中一种强大的编程机制,它允许我们将函数作为参数传递给其他函数。这种设计模式在现代编程中极为常见,特别是在事件驱动编程、异步操作和模块化设计中。1.1 回调函数的本质回调函数本质上是一个通过函数指针调用的…...
Fiji图像处理软件更新故障排查指南:当科学工具遇到“升级烦恼“
Fiji图像处理软件更新故障排查指南:当科学工具遇到"升级烦恼" 【免费下载链接】fiji A "batteries-included" distribution of ImageJ :battery: 项目地址: https://gitcode.com/gh_mirrors/fi/fiji Fiji作为生物图像分析领域的瑞士军刀…...
如何用Mi-Create实现小米穿戴设备表盘个性化设计?
如何用Mi-Create实现小米穿戴设备表盘个性化设计? 【免费下载链接】Mi-Create Unofficial watchface creator for Xiaomi wearables ~2021 and above 项目地址: https://gitcode.com/gh_mirrors/mi/Mi-Create Mi-Create是一款专为2021年及以后发布的小米穿戴…...
IQR四分位数法是什么?
一、核心概念:四分位数与IQR1. 四分位数(Quartiles)将一组有序数据(从小到大排列)划分为4个相等部分的三个关键分割点,分别记为:Q1(第一四分位数,25%分位数)&…...
GME-Qwen2-VL-2B自动化测试:基于模型视觉理解的GUI界面测试脚本
GME-Qwen2-VL-2B自动化测试:基于模型视觉理解的GUI界面测试脚本 1. 引言 你有没有遇到过这样的场景?辛辛苦苦写了一套UI自动化测试脚本,结果软件界面稍微改个按钮颜色、挪个位置,整个测试就全挂了。维护成本高得吓人,…...
Zotero Connector进阶:定制知乎内容抓取与快照/正文模式切换详解
1. 为什么需要定制知乎内容抓取? 作为一款强大的文献管理工具,Zotero在学术论文管理方面表现出色,但在处理知乎这类内容平台时却常常力不从心。我最初使用Zotero Connector抓取知乎内容时,经常遇到只保存了网页快照而无法获取完整…...
从原理到实践:深入理解Shellcode免杀技术及其对抗策略
Shellcode免杀技术的深度解析与对抗策略演进 在网络安全攻防对抗的永恒博弈中,Shellcode免杀技术始终占据着特殊地位。不同于传统的恶意软件检测规避,Shellcode免杀更注重代码层面的"隐形"能力,其核心在于让关键载荷在内存中执行时…...
