学习单向链表带哨兵demo

【数据结构】有无哨兵头节点的单链表,便利头插尾插,以LeetCode两道题为例 有无哨兵头节点的单链表 ❤️❤️ 笔记指路gitee:❤️:hear有无哨兵节点单链表 ❤️❤️ 欢迎 白嫖!!????????????????❤️❤️ 文章目录有无哨兵头节点的单链表一、什么是哨兵节点?二、无哨兵节点三、有哨兵节点 ????????最近在做题的过程中,遇到不同的单链表题目,在这些题目中,如果设置一个哨兵头节点可能会带来一定的便利 在之前的单链表增删查改中就发现,头插其实比尾插简单 ????????文章指路:【数据结构】单链表的增删查改,附代码+笔记gitee自取_Rinne’s bl 阅读详情

一、定义

在计算机科学中,链表是数据元素的线性集合,其每个元素都指向下一个元素,元素存储上并不连续。

1.可以分三类为

  • 单向链表,每个元素只知道其下一个元素是谁

  • 双向链表,每个元素知道其上一个元素和下一个元素

  • 循环链表,通常的链表尾节点tail指向的都是null,而循环链表的tail指向的是头节点head

链表内还有一种特殊的节点称为哨兵节点,也叫做哑元(Dummy)节点,它不存储数据,通常用作头尾,用来简化边界判断,如下图所示:

2.性能

随机访问

根据index查找,时间复杂度O(n)

插入或删除

起始位置:O(1)

结束位置:如果已知tail尾节点是O(1),不知道tail尾节点是O(n)

中间位置:根据index查找时间 + O(1)

二、单向链表的插入、删除、查找示例

1.以下为不带哨兵节点代码

package com.tfq.arithmetic.linkedlist;

import java.util.Iterator;
import java.util.function.Consumer;

/**
 * @author: fqtang
 * @date: 2024/05/18/19:23
 * @description: 单向链表类
 */
public class SingleLinkedList implements Iterable<Integer> {
	/**
	 * 头指针
	 */
	private Node head = null;

	public void addFirst(int value) {
		//1.链表为空
//		head = new Node(value,null);
		//2.链表非空
		head = new Node(value, head);
	}

	/**
	 * 返回链表第一种方式
	 *
	 * @return
	 */
	public void loop(Consumer<Integer> consumer) {
		Node p = head;
		while(p != null) {
			consumer.accept(p.value);
			p = p.next;
		}
	}

	/**
	 * 返回链表第二种方式
	 *
	 * @return
	 */
	public void loop2(Consumer<Integer> consumer) {
		for(Node p = head; p != null; p = p.next) {
			consumer.accept(p.value);
		}
	}


   /**
	 * 返回链表第三种方式
	 *
	 * @return
	 */
	public void loop3() {
		recursion(head);
	}

	/**
	 * 递归链表
	 *
	 * @return
	 */
	public void recursion(Node curr) {
		if(curr == null) {
			return;
		}
		System.out.println(curr.value);
		recursion(curr.next);
	}


	/**
	 * 返回链表第四种方式
	 *
	 * @return
	 */
	@Override
	public Iterator<Integer> iterator() {
		//匿名内部类 ->带名字内部类
		return new NodeIterator();
	}

	private Node findLast() {
		if(head == null) {//空链表
			return null;
		}

		Node p = head;
		while(p.next != null) {
			p = p.next;
		}
		return p;
	}

	public void addLast(int value) {
		Node last = findLast();
		if(last == null) {
			addFirst(value);
			return;
		}
		last.next = new Node(value, null);
	}

	private Node findNode(int index) {
		int i = 0;
		for(Node p = head; p != null; p = p.next, i++) {
			if(index == i) {
				return p;
			}
		}
		return null;//没找到
	}

	public int getValue(int index) {
		Node node = findNode(index);
		if(node == null) {
			//抛异常
			throw new IllegalArgumentException(String.format("index [%d] 不合法%n", index));
		}
		return node.value;
	}

	/**
	 * 向索引位置插值
	 *
	 * @param index 索引
	 * @param value 待插入值
	 */
	public void insert(int index, int value) {
		if(index == 0) {//若index=0,则向链表的head添加元素节点
			addFirst(value);
			return;
		}
		//找索引的上一个节点
		Node prev = findNode(index - 1);
		if(prev == null) {//上一个节点没找到
			throw new IllegalArgumentException(String.format("index [%d] 不合法%n", index));
		}
		prev.next = new Node(value, prev.next);
	}

	/**
	 * 删除第一个节点
	 */
	public void removeFirst() {
		if(head == null) {
			throw new IllegalArgumentException(String.format("index [%d] 不合法%n", 0));
		}
		head = head.next;
	}

	/**
	 * 根据索引删除指定元素
	 * @param index 待删除索引
	 */
	public void remove(int index) {
		if(index ==0){
			removeFirst();
			return;
		}
		//找到上一个节点
		Node prev = findNode(index - 1);
		if(prev == null){
			throw new IllegalArgumentException(String.format("index-1 [%d] 不合法%n", index));
		}
		//被删除的节点
		Node removed = prev.next;
		if(removed == null){
			throw new IllegalArgumentException(String.format("index [%d] 不合法%n", index));
		}
		//上一个节点指向被删除节点的下一个节点
		prev.next = removed.next;
	}

	/**
	 * 节点类,单向链表与节点是外部类与内部类,更好隐藏内部类,
	 * 方便外部调用,不需要外部知道内部实现细节
	 * 内部类使用了static,则此内部类没有使用外部类的变量。当内部类没有使用外部类的变量时推荐使用static
	 */
	private static class Node {
		/**
		 * 值
		 */
		int value;
		/**
		 * 下一个结点指针
		 */
		Node next;

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

	}

	/**
	 * 内部类使用了一个外部类的成员变量时在内部类的类名上不能使用static,内部类使用了一个Node
	 */
	private class NodeIterator implements Iterator<Integer> {
		Node p = head;

		@Override
		public boolean hasNext() {//是否有下一个元素
			return p != null;
		}

		@Override
		public Integer next() {//返回当前元素值,并指向下一个元素
			int v = p.value;
			p = p.next;
			return v;
		}
	}
}

2.以下为代哨兵节点

哨兵用来简化边界判断(简化查找上一个节点和非空判断)简化代码。

package com.tfq.arithmetic.linkedlist;

import java.util.Iterator;
import java.util.function.Consumer;

/**
 * @author: fqtang
 * @date: 2024/05/18/19:23
 * @description: 单向链表类(带哨兵监视)
 */
public class SingleLinkedListSentinel implements Iterable<Integer> {
	/**
	 * 头指针指向哨兵节点
	 */
	private Node head = new Node(666, null);

	public void addFirst(int value) {
		insert(0,value);
	}

	/**
	 * 返回链表第一种方式
	 *
	 * @return
	 */
	public void loop(Consumer<Integer> consumer) {
		Node p = head.next;
		while(p != null) {
			consumer.accept(p.value);
			p = p.next;
		}
	}

	/**
	 * 返回链表第二种方式
	 *
	 * @return
	 */
	public void loop2(Consumer<Integer> consumer) {
		for(Node p = head.next; p != null; p = p.next) {
			consumer.accept(p.value);
		}
	}

	/**
	 * 返回链表第三种方式
	 *
	 * @return
	 */
	@Override
	public Iterator<Integer> iterator() {
		//匿名内部类 ->带名字内部类
		return new NodeIterator();
	}

	private Node findLast() {
		Node p = head;
		while(p.next != null) {
			p = p.next;
		}
		return p;
	}

	public void addLast(int value) {
		Node last = findLast();
		last.next = new Node(value, null);
	}

	private Node findNode(int index) {
		int i = -1;
		for(Node p = head; p != null; p = p.next, i++) {
			if(index == i) {
				return p;
			}
		}
		return null;//没找到
	}

	public int getValue(int index) {
		Node node = findNode(index);
		if(node == null) {
			//抛异常
			throw new IllegalArgumentException(String.format("index [%d] 不合法%n", index));
		}
		return node.value;
	}

	public void test() {
		int i = 0;
		for(Node p = head; p != null; p = p.next, i++) {
			System.out.println(p.value + ",索引值:" + i);
		}
	}

	/**
	 * 向索引位置插值
	 *
	 * @param index 索引
	 * @param value 待插入值
	 */
	public void insert(int index, int value) {
		//找索引的上一个节点
		Node prev = findNode(index - 1);
		if(prev == null) {//上一个节点没找到
			throw new IllegalArgumentException(String.format("index [%d] 不合法%n", index));
		}
		prev.next = new Node(value, prev.next);
	}

	/**
	 * 删除第一个节点
	 */
	public void removeFirst() {
		remove(0);
	}

	/**
	 * 根据索引删除指定元素
	 *
	 * @param index 待删除索引
	 */
	public void remove(int index) {
		//找到上一个节点
		Node prev = findNode(index - 1);
		if(prev == null) {
			throw new IllegalArgumentException(String.format("index-1 [%d] 不合法%n", index));
		}
		//被删除的节点
		Node removed = prev.next;
		if(removed == null) {
			throw new IllegalArgumentException(String.format("index [%d] 不合法%n", index));
		}
		//上一个节点指向被删除节点的下一个节点
		prev.next = removed.next;
	}

	/**
	 * 节点类,单向链表与节点是外部类与内部类,更好隐藏内部类,
	 * 方便外部调用,不需要外部知道内部实现细节
	 * 内部类使用了static,则此内部类没有使用外部类的变量。当内部类没有使用外部类的变量时推荐使用static
	 */
	private static class Node {
		/**
		 * 值
		 */
		int value;
		/**
		 * 下一个结点指针
		 */
		Node next;

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

	}

	/**
	 * 内部类使用了一个外部类的成员变量时在内部类的类名上不能使用static,内部类使用了一个Node
	 */
	private class NodeIterator implements Iterator<Integer> {
		Node p = head.next;

		@Override
		public boolean hasNext() {//是否有下一个元素
			return p != null;
		}

		@Override
		public Integer next() {//返回当前元素值,并指向下一个元素
			int v = p.value;
			p = p.next;
			return v;
		}
	}
}

若要学习双向链表,请查看我下一章节。

c语言单链表demo 对单链表的建立、输出、统计、查找、插入、删除做了详细的分析与编码的小demo 立即下载

相关推荐

FID指标实战:如何用Python快速计算生成图像的质量分数(附完整代码)

本文详细介绍了如何使用Python计算Fréchet Inception Distance(FID)指标,以评估生成图像的质量。通过完整的代码示例和优化技巧,帮助开发者快速实现FID计算,并解读分数背后的实际意义,适用于GAN和扩散模型的性能评估。

weixin_28718487的博客 374

单向链表实例

本篇也是一道应用Java面向对象基础篇知识的程序实例,通过理解掌握小模型实例,为日后进行实际开发奠定基础 链表 就是链式地存储数据,是一种常见的数据结构。链表就好比火车,一节跟着一节 有以下一道程序: class Node { //定义节点 private String data; private Node next; //保存下一个节点 pu...

Rachel_pan的博客 686

从零到一:RF-DETR如何通过神经架构搜索重塑实时目标检测的边界

RF-DETR通过创新的权重共享神经架构搜索(NAS)机制,重新定义了实时目标检测的技术边界。这项技术在COCO等基准测试中表现卓越,成为首个AP超过60的实时检测器,同时保持跨领域稳定性和高效推理速度。RF-DETR的动态架构调整能力使其能够适应从嵌入式设备到云端服务器的各种硬件环境,为工业质检、自动驾驶等场景提供灵活解决方案。

weixin_29061531的博客 467

单向链表在C语言上的实现

作为几乎是初学C语言的人,在指针的问题还没有完全搞明白的时候,就看到了数据结构里面链表这样一个庞然大物,完全不懂的怎么操作。在阅读了多方资料后,终于根据自己的理解,写出了第一个单向链表程序,放在此处与大家分享,也让自己在复习的时候可以再回顾一下。程序实现了链表的创建、添加单个元素、删除单个元素的功能;对象为单个int型,即程序中的data。因为都是用的函数,所以如果要添加其他的操作,只需要写另外的...

go_sann 2656

GNSS差分码偏差(DCB)从原理到实战:如何避免数据处理中的常见错误

本文深入解析GNSS差分码偏差(DCB)的原理及其对高精度定位的关键影响。针对数据处理中因忽略DCB校正导致的常见错误,如单系统PPP高程偏差、多系统融合精度下降及电离层研究失真等,提供了实战解决方案与主流DCB产品获取、集成方法,帮助用户有效规避陷阱,提升数据处理精度。

k5l6m的博客 223

Vivado安装教程:多版本共存的配置技巧

详解Vivado安装教程中的多版本共存配置技巧,帮助用户在同一系统中管理不同版本的Vivado开发环境,提升开发效率与兼容性,适用于需要切换版本的FPGA开发者。

weixin_35257663的博客 1768

单向链表和双向链表demo

链表介绍 链表优劣势:插入和删除速度快,查询速度慢 链表分类: 单向链表:只有头结点,每个节点可以指向下一个节点 双向链表:有头结点和尾结点,每个节点不仅可以指向下一个节点,还可以指向上一个节点 单向链表 1、node节点 package com.springboot.demo.linked; import lombok.AllArgsConstructor; import lo...

大龙的博客 457

GameFramework框架详解之 DataTable配置表模块

一.前言: DataTable模块是项目的配置表模块,配置表一般是从excel导出csv/json/xml格式的,之所以是excel作为源文件,原因是因为策划同学使用配表最方便。然后一键导出到项目中,除了csv/json/xml配置文件外,一般还会伴随着导出配置的实体类,这样研发同学就可以使用了。GameFramework的DataTable模块也是秉持着这种一贯的策略来实现的。 二.DataRow DataRow在excel数据表中,对应的是一行数据,而在代码中对应的其实就是一条数据对象。 接口IData

TxNet 4722

单向链表demo

class StuNode(val pno:Int,val pname:String){ val no:Int=pno var name: String = pname var next:StuNode=null //下节点,默认null } /** * 说明:只是demo用于理解,有很多漏洞,具体根据应用场景解决 */ class SingleLinkList{ //...

weixin_37589026的博客 243

checking OpenSSL library version... configure: error: OpenSSL >= 1.1.1 required (have “100020bf 解决方案

 本文主要介绍了checking OpenSSL library version… configure: error: OpenSSL >= 1.1.1 required (have “100020bf (OpenSSL 1.0.2k-fips 26 Jan 2017)”)解决方案,希望能对使用Linux的同学们有所帮助。 文章目录 1. 问题描述 2. 解决方案

weixin_43178406的博客 2万+

Claude Code 100个真实案例 - 用AI批量清洗Excel脏数据(10万行3分钟搞定)

本案例展示如何使用 Claude Code 搭建一个强大的 Excel 数据清洗工具。支持自动检测和处理空值、重复行、格式错误、异常值等常见脏数据问题,批量处理10万行数据只需3分钟。

yueyeguzhuo的专栏 37

【数据结构】基于比较的排序算法 - 交换排序

本文介绍交换排序中的冒泡排序与快速排序。冒泡排序通过相邻元素比较交换,实现升序,时间复杂度最坏为O(N²),空间复杂度O(1),稳定且适合小规模有序数据。快速排序采用分治思想,以基准值划分区间,平均时间复杂度O(N log N),空间复杂度O(log N),但不稳定,实际应用中性能优异,可通过随机基准、小区间优化等提升效率。

careathers的博客 316

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

pandas 深度入门:从数据结构到一份真实销售报表

本文系统讲解Pandas核心应用,从三分钟安装到完整月度报表生成,涵盖数据读取、清洗、筛选、分组聚合、表连接、透视表与时间序列等13个关键环节。强调“先看数据再动手”,掌握Series/DataFrame/Index结构本质,熟练运用loc/iloc、merge校验、agg/transform区别,通过真实案例实现高效报表自动化,告别手动Excel操作。最后提供性能优化与高频报错解决方案,助力读者快速上手,真正实现“写二十行代码,跑三秒出结果”。

信息安全领域优质创作者|操作系统领域专家 241

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

工单系统的数据结构化:故障分类体系设计与报表可用性

很多团队上完工单系统半年,会发现一个尴尬的事实:单子照常在走,报表却始终出不来。导出的 Excel 几千行,看不出趋势、算不出口径、答不了"哪类故障占大头"。问题几乎都不在系统,而在数据模型的第一层——。这篇文章把这件事拆开讲:自由文本为什么会失效、分类体系怎么分层、表怎么建、老数据怎么迁、上线前怎么验收。

ITyunwei0987的博客 266

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

【Linux进程(二)】深入 Linux 内核:进程状态切换与底层数据结构的秘密

本文从操作系统理论与Linux内核源码双重视角,深入解析进程状态的底层机制。通过“侵入式链表”与“偏移量计算”揭示task_struct如何同时存在于多类数据结构中,实现高效调度与管理。结合ps命令与实战代码,详解R、S、T、Z、X等状态的成因与行为,涵盖前后台切换、暂停/恢复、僵尸进程、孤儿进程等典型场景,揭示进程生命周期的完整图景,实现“知其然更知其所以然”。

wacsii的博客 289

数据结构:9.Map和Set

有的讲解会说这里key是标识作用,这种说法其实没有太大意义,就算知道了他是标识value的作用又如何,在实际的使用过程中,我们只会根据具体的效果选择使用的方法等,不会执着于“标识”这种说法。2.由于哈希函数的设计原则太过理论,它仅仅是在认为:这些通过关键码算出来的index不同 的情况下设计的,然而这种理论化的设计原则在遇到冲突时,恰恰就出现了问题,而我们能做的,就是降低冲突率。虽然哈希表的使用过程中存在冲突,但是在实际的使用过程中,哈希表的冲突率是不高的,冲突的个数是可控的。

2602_95646088的博客 183

[C++高阶数据结构·图论下] 最短路径:为什么“挑最小的”一定正确?

本文系统对比最小生成树与最短路径的核心差异,聚焦三类最短路算法:Dijkstra(非负权、贪心确认)、Bellman-Ford(可负权、反复松弛)、Floyd(全源、分层中转)。核心区别在于:MST 关注“连通所有点”的最小代价(边数固定),而最短路关注“两点间路径最短”(边数可变)。关键反直觉点是:负权对 MST 无害,却使 Dijkstra 失效——因“绕远更近”破坏贪心前提。算法本质反映三种处理不确定性的策略:确认式(Dijkstra)、迭代修正(Bellman-Ford)、分层逼近(Floyd)。强

Tairitsu_青云 499

[C++高阶数据结构·图论上] 最小生成树:为什么“选边”和“选点”是两条路?

本文系统讲解最小生成树(MST)的核心思想与两大算法:Kruskal与Prim。强调MST目标是用最小权值总和连接所有顶点形成一棵树,而非单边最小。通过三条构造准则(用图中边、恰n-1条边、无环)统一框架。Kruskal按边贪心,依赖并查集判环,适合稀疏图;Prim按点扩张,利用割集特性天然避免成环,需起点,适合稠密图。两算法本质差异在于“判环”机制与数据结构选择,且与最短路径问题在连通性、负权处理上截然相反,需分篇厘清。

Tairitsu_青云 205

Vue3 低代码移动表单设计器实战(二):缩放画布下的拖拽系统与树状级联数据结构

本文深入剖析Vue3低代码表单设计器在缩放画布下的拖拽失效问题,揭示SortableJS在CSS Transform下坐标崩溃的数学根源。通过自研独立视觉克隆壳层与逆向几何补偿算法,实现精准拖拽;采用扁平数据存储+运行时树状计算结构,解决级联依赖与深克隆难题,确保逻辑一致性与性能优化,为复杂低代码系统提供可复用的工程范式。

NanChen的博客 331

排序算法深度笔记(上):比较类排序的思想演进与代码实现

与。本文聚焦前者 —— 也就是通过元素间两两比较来确定相对顺序的排序算法,从最朴素的 O (n²) 基础排序,到工业级主流的 O (nlogn) 进阶排序,逐层拆解设计思想、带注释的代码实现与边界细节。当我们理解了比较排序的性能边界,也就自然引出了非比较排序的存在意义。所有代码均遵循统一的实现规范:入口函数前置空数组与单元素边界校验,基于数组原地修改,主函数附带可直接运行的测试用例,与练习代码风格完全对齐。

2401_87069538的博客 475

【数据结构】静态 & 动态顺序表

顺序存储来实现线性表:链式存储实现线性表:存放在一段,数据元素之间的逻辑关系由物理存储关系体现:存储在,数据元素之间的逻辑关系用来表示:且大小固定()的数组:可以自动调节大小()的数组。

2301_80694580的博客 183

【数据结构-3】算法美学之降复杂度

在while循环的循环体内,首先用临时变量保存最后一个位置的数据,再通过for循环把所有元素往后移动一位,最后把tmp中保存的值放到下标为0的位置上去。方法二的思路是:创建新数组,让原数组中后k个数据移动到新数组中前面的位置,让原数组中前numsSize-k个数据移动到新数组中后面的位置,这。这段代码的思路是:三次逆置,第一次逆置前numsSize-k个数据,第二次逆置后k个数据,最后整体逆置。这篇博文分享一道解法不一的算法题,求解过程的目的在于降低算法的时间复杂度,题源力扣。,属于空间换时间的操作。

weixin_44218929的博客 77

数据结构:双向链表实现,链表与顺序表的对比

本节讲解数据结构双向链表,链表和顺序表的特点对比,顺便解释了一下什么是CPU缓存和内存碎片

lei_jiale的博客 364

【数据结构-2】顺序表的实现

线性表是一种数据结构,它分为逻辑结构和物理结构,在逻辑结构上线性表是呈线性的,数据是是连续存储的;在物理结构(物理空间)上,线性表不一定是连续存储的。线性表的实现,主要分为数组和链式结构。顺序表是线性表的一种实现方式,顺序表在逻辑结构和物理结构上都是连续的。顺序表的底层实现是数组。

weixin_44218929的博客 202

数据结构—B树

一棵m(m>2)阶的B树,是一棵平衡的M路平衡树,可以说空树或满足以下6点。1.根节点至少有两个孩子2.每个分支节点都包含k-1个关键字和k个孩子,ceil(m/2)<=k<=m;孩子比关键字多一个ceil向上取整3.每个叶子节点都包含k-1个关键字,ceil(m/2)<=k<=m;4.所有的叶子节点在同一层。5.每个节点中的关键字从小到大排列,节点当中k-1个元素正好是k个孩子包含元素的值域划分。6.节点的结构是{n,A0,K1,A1,K2,........Kn,An};

fish_xk的博客 122
上一篇: 谷粒商城ThirdPartyApplication启动报错: Oss endpoint can‘t be empty解决总结
下一篇: 学习双向链表带哨兵demo
developerFBI
博客等级 码龄15年 174粉丝 105原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值