学习单向链表带哨兵demo

一、定义

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

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;
		}
	}
}

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

抖音小店不能做无货源了吗?当然不是,而是玩法更先进了!

大家好&#xff0c;我是电商糖果 自从2023年抖音小店开始严查无货源&#xff0c;不少商家被平台处罚&#xff0c;被逼无奈退出抖音小店。 网上关于抖音小店不能做无货源的声音越来越多。 可是一年多过去&#xff0c;大家渐渐的发现&#xff0c;平台内还是有很多无货源商家&a…

Sping源码(八)—registerBeanPostProcessors

序言 之前我们用大量的篇幅介绍过invokeBeanFactoryPostProcessors()方法的执行流程。 而invokeBeanFactoryPostProcessors的主要逻辑就是遍历执行实现了BeanDefinitionRegistryPostProcesso类(主要是针对BeanDefinition的操作)和BeanFactoryPostProcessor(主要针对BeanFacrot…

spring-boot集成slf4j(二)logback配置详解

一、configuration 根节点&#xff1a;configuration&#xff0c;作为顶级标签&#xff0c; 可以用来配置一些lockback的全局属性&#xff0c;常见的属性如下&#xff1a; &#xff08;1&#xff09;scan“true” &#xff1a;scan是否开启自动扫描&#xff0c;监控配置文件更…

XShell-连接-Centos 7

XShell 连接Centos 7 一.准备 安装XShell XShell下载地址&#xff1a; 在虚拟机上安装Centos 7&#xff0c;具体操作自行学习 二.Centos 7的准备 1.网络适配器修改为NAT 2.获取IP 输入命令&#xff1a; ip addr我的Centos 7对外IP为192.168.174.129 三.XShell连接Cento…

Big Demo Day第十三期活动即将启幕,Web3创新项目精彩纷呈,PEPE大奖等你抽取

5月28号在香港数码港 Big Demo Day第十三期 活动即将拉开帷幕&#xff0c;活动将汇集众多Web3领域的创新项目&#xff0c;为参会者带来一场科技与智慧交融的盛宴。在这里&#xff0c;你不仅能深入了解区块链、AI等前沿技术的最新应用&#xff0c;还能有机会赢取丰厚的PEPE大奖。…

使用maven-helper插件解决jar包冲突

发现问题 maven-helper分析问题 如上所述&#xff0c;问题就是依赖版本冲突了&#xff0c;出现版本冲突的原因是因为由于Maven具有依赖传递性&#xff0c;所以当你引入一个依赖类的同时&#xff0c;其身后的依赖类也一起如过江之鲫纷至沓来了。 举个例子&#xff1a;   A依赖…

保护元件-详实的熔断器(保险丝)知识

目录&#xff1a; 一、汽车保险丝设计与选型 1、概述 2、构造及工作原理 1&#xff09;构造 2&#xff09;工作原理 3&#xff09;保险丝熔断及分断时间 4&#xff09;时间/电流特性曲线 5&#xff09;环境温度修正系数 3、熔化热能值I2t★ 4、三种电流模型 1&a…

废物回收机构|基于SprinBoot+vue的地方废物回收机构管理系统(源码+数据库+文档)

地方废物回收机构管理系统 目录 基于SprinBootvue的地方废物回收机构管理系统 一、前言 二、系统设计 三、系统功能设计 1管理员功能模块 2 员工功能模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍…

瑞芯微RV1126——人脸识别框架分析

项目核心是在Linux平台上利用摄像头采集人脸&#xff0c;并进行人脸识别。这个项目使用的是FFMPEGOPENCV虹软框架完成。 FFMPEG的主要工作是负责采集摄像头的数据并把摄像头数据发送给opencv。 Opencv的主要工作则是把摄像头数据转换成矩阵数据。 虹软的主要功能则是利用Open…

K8s 二进制部署---下篇(多master节点 负载均衡 高可用)

一 master02 节点部署 master01192.168.11.5kube-apiserver&#xff0c;kube-controller-manager&#xff0c;kube-scheduler&#xff0c;etcdmaster02192.168.11.12kube-apiserver&#xff0c;kube-controller-manager&#xff0c;kube-scheduler&#xff0c;etcdnode01192.1…

WebGL渲染引擎优化方向——渲染帧率的优化

作者&#xff1a;caven chen 对此内容感兴趣还可以看前文&#xff1a; WebGL渲染引擎优化方向——加载性能优化 前言 WebGL 是一种强大的图形渲染技术&#xff0c;可以在浏览器中快速渲染复杂的 3D 场景。但是&#xff0c;由于 WebGL 的高性能和高质量要求&#xff0c;如果…

白嫖免费图床!CloudFlare R2太香了!

1 为啥要折腾搭建一个专属图床&#xff1f; 技术大佬写博客都用 md 格式&#xff0c;要在多平台发布&#xff0c;图片就得有外链后续如博客迁移&#xff0c;国内博客网站如掘金&#xff0c;简书&#xff0c;语雀等都做了防盗链&#xff0c;图片无法迁移 2 为啥选择CloudFlare…

记录一个写SpringBoot中Hive数据可以正常提取但无法存到MySQL的bug

【背景】 我正在用SpringBoot框架写一个数据治理项目&#xff0c;目前所处阶段是将hive和hdfs中的元数据提取出来&#xff0c;存储到MySQL中&#xff0c;我的hive和hdfs上的数据存储在三台Linux服务器上&#xff08;hadoop102-104&#xff09;&#xff0c;MySQL在我本地Window…

运行vue2项目基本过程

目录 步骤1 步骤2 步骤3 补充&#xff1a; 解决方法&#xff1a; node-scss安装失败解决办法 步骤1 安装npm 步骤2 切换淘宝镜像 #最新地址 淘宝 NPM 镜像站喊你切换新域名啦! npm config set registry https://registry.npmmirror.com 步骤3 安装vue-cli npm install…

【Python进阶】主流电商平台数据分析||数据采集返回商品详情主题链接主图SKU数据

Python是一种高级编程语言&#xff0c;广泛应用于软件开发、数据分析、人工智能、科学计算等领域。在软件开发方面&#xff0c;Python在网站开发、网络编程、桌面软件开发等方面有着广泛的应用。在数据分析和人工智能领域&#xff0c;Python的各种库如NumPy、Pandas、Matplotli…

小程序开发的基本用法

一:基本组件 1.view和scroll-view view等同于div,view写在小程序显示和div一样的效果. srcoll-view scroll-x/scroll-y是div能移动的.但是小程序没有显示大的划的. 且scroll-view才能实现这个,要这个组件且要属性,内部基本结构才能实现. view没有属性实现. 2.swiper和swi…

温故而知新-秒杀项目篇【面试复习】

温故而知新-秒杀项目篇【面试复习】 前言版权推荐温故而知新-论坛项目篇【面试】秒杀项目中注册模块怎么实现的&#xff1f;秒杀项目中登录模块怎么实现的&#xff1f;秒杀项目中显示登录用户信息怎么实现的&#xff1f;SessionStorage是什么?为什么不用session而用token什么是…

初识C语言——第二十五天

函数的嵌套调用和链式访问 函数不可以嵌套定义&#xff0c;但可以嵌套调用 链式访问&#xff1a;把一个函数的返回值作为另外一个函数的参数 #define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h>//写一个函数&#xff0c;每调用一次这个函数&#xff0c;就会 将num…

数据结构之时间复杂度和空间复杂度的相关计算

找往期文章包括但不限于本期文章中不懂的知识点&#xff1a; 个人主页&#xff1a;我要学编程(ಥ_ಥ)-CSDN博客 所属专栏&#xff1a;数据结构&#xff08;Java版&#xff09; 目录 时间复杂度 概念 大O的渐进表示法 相关练习 例1&#xff1a; 例2&#xff1a; 例3&am…

可转债日内自动T+0交易,行情推送+策略触发+交易接口

说明 目前这个项目已编译打包,下载即可测试,直接生成多平台可执行文件&#xff0c;详见运行方法。行情部分与策略弱相关&#xff0c;拆分解耦单独作为一个项目。行情项目请移步GitHub - freevolunteer/hangqing: A股行情订阅工具&#xff0c;支持股票/可转债level2/level2数据&…