学习单向链表带哨兵demo

news2024/9/20 20:27:45

一、定义

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

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

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

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

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

相关文章

mySql从入门到入土

基础篇 在cmd中使用MYSQL的相关指令&#xff1a; net start mysql // 启动mysql服务 net stop mysql // 停止mysql服务 mysql -uroot -p1234//登录MYSQL&#xff08;-u为用户名-p为密码&#xff09; //登录参数 mysql -u用户名 -p密码 -h要连接的mysql服务器的ip地址(默认1…

记一次安卓“Low on memory“崩溃问题

前言 最近再调人脸识别算法相关demo,发现调试期间总是偶发性崩溃&#xff0c;捕获不到异常的那种&#xff0c;看日志发现原因是Low on memory&#xff0c;一开始还疑惑 App内存不够应该是OOM啊,怎么会出现这种问题&#xff0c;百思不得其解&#xff0c;直到我打开了 Android s…

Git 仓库中 -- 代码冲突产生、定位、解决的流程

目录 前置知识1 工具环境2 冲突的产生2.1 仓库中的源代码2.2 人员 A 首先更改代码2.3 人员 B 更改代码&#xff0c;产生冲突2.3.1 第一次错误提示&#xff1a;2.3.2 第二次错误提示&#xff1a; 3 查看冲突4 手动解决冲突4.1 方式一4.2 方式二&#xff08;tortoisegit&#xff…

Vitis HLS 学习笔记--控制驱动任务示例

目录 1. 简介 2. 代码解析 2.1 kernel 代码回顾 2.2 功能分析 2.3 查看综合报告 2.4 查看 Schedule Viewer 2.5 查看 Dataflow Viewer 3. Vitis IDE的关键设置 3.1 加载数据文件 3.2 设置 Flow Target 3.3 配置 fifo 深度 4. 总结 1. 简介 本文对《Vitis HLS 学习…

CSAPP(datalab)解析

howManyBits /* howManyBits - 返回用二进制补码表示x所需的最小位数* 示例: howManyBits(12) 5* howManyBits(298) 10* howManyBits(-5) 4* howManyBits(0) 1* howManyBits(-1) 1* howManyBits(0x80000000) …

【Linux】TCP协议【上】{协议段属性:源端口号/目的端口号/序号/确认序号/窗口大小/紧急指针/标记位}

文章目录 1.引入2.协议段格式4位首部长度16位窗口大小32位序号思考三个问题【demo】标记位URG: 紧急指针是否有效提升某报文被处理优先级【0表示不设置1表示设置】ACK: 确认号是否有效PSH: 提示接收端应用程序立刻从TCP缓冲区把数据读走RST: 对方要求重新建立连接; 我们把携带R…

【C++】详解AVL树——平衡二叉搜索树

个人主页&#xff1a;东洛的克莱斯韦克-CSDN博客 祝福语&#xff1a;愿你拥抱自由的风 目录 二叉搜索树 AVL树概述 平衡因子 旋转情况分类 左单旋 右单旋 左右双旋 右左双旋 AVL树节点设计 AVL树设计 详解单旋 左单旋 右单旋 详解双旋 左右双旋 平衡因子情况如…

远程工作的数据安全挑战和解决策略

随着远程工作的普及&#xff0c;数据安全面临了前所未有的挑战。企业在应对这些挑战时&#xff0c;必须采取切实有效的策略来保护敏感信息。以下是远程工作数据安全的主要挑战和相应的解决策略&#xff1a; 数据安全挑战 设备丢失或被盗&#xff1a;员工在外工作时&#xff0c…

从 0 开始本地部署大语言模型

1、准备 ● Ollama&#xff1a;ollama.com ● Docker&#xff1a;https://docs.openwebui.com/ 2、下载 Ollama 进入 Ollama 官网&#xff0c;点击 Download 。 下载完成后&#xff0c;双击安装&#xff0c;什么都不需要勾选&#xff0c;直接下一步即可。安装完成&#xf…

算法之堆排序

堆排序是一种基于比较的排序算法&#xff0c;通过构建二叉堆&#xff08;Binary Heap&#xff09;&#xff0c;可以利用堆的性质进行高效的排序。二叉堆是一个完全二叉树&#xff0c;可以有最大堆和最小堆两种形式。在最大堆中&#xff0c;父节点的值总是大于或等于其子节点的值…

【TB作品】stm32单片机读取DS2401程序

DS2401是由Analog Devices公司生产的一种硅序列号芯片&#xff0c;它提供了一个绝对唯一的64位ROM识别码&#xff0c;用于确保可追溯性。以下是对DS2401器件的分析&#xff1a; 特点和优势&#xff1a; 唯一性&#xff1a;每个DS2401芯片都有一个独一无二的64位注册码&#x…

Redis机制-Redis缓存穿透,击穿,雪崩理解等问题的理解和学习

目录 一 缓存穿透问题 二 缓存击穿问题 三 缓存雪崩问题&#xff1a; 图1 正常的Redis缓存流程 一 缓存穿透问题 我们都知道Redis是一个存储键值对的非关系型数据库&#xff0c;那么当用户进行查询的时候&#xff0c;势必会从前端发起请求&#xff0c;从而数据从Redis缓存…

MySQL笔记第三天(从小白到入门)

文章目录 MySQL笔记SQL语言介绍数据库系统关系型数据库非关系型数据库SQL和数据库系统的关系数据库系统架构 MySQL的介绍概念MySQL的版本 MySQL的DDL操作-重点基本数据库操作基本表操作 MySQL的DML操作-重点insert-插入数据update-更新数据delete-删除数据 MySQL的约束-了解概述…

RabbitMQ .NET

setup rabbitmq docker run --namerabbit -p 15672:15672 -p 5672:5672 -e RABBITMQ_DEFAULT_USERadmin -e RABBITMQ_DEFAULT_PASSxxx -d rabbitmq:management ip:15672 login nuget RabbitMQ.Client Send //1.1.实例化连接工厂 var factory new ConnectionFactory() …

WordPress搭建流程

1. 简介 WordPress 是一个 PHP 编写的网站制作平台。WordPress 本身免费,并且拥有众多的主题可以使用,适合用于搭建个人博客、公司官网、独立站等。 2. 环境准备 2.1 WordPress 下载 WordPress 可以在 Worpress中文官网 下载(如果后续要将后台调成中文的话,一定要从中文…

虚拟局域网(VLAN)

关键词&#xff1a;veth、vlan、bridge、iptables、nat、tcpdump、icmp、cidr、arp、路由表、计算机网络协议栈 前言 在过去的几十年里&#xff0c;互联网发展得非常快。许多新兴技术迅速崛起&#xff0c;也有不少曾经的主流技术被淘汰。然而&#xff0c;有些技术因为其基础性…

sqlserver 创建表,列及表,列描述

-- 创建表 CREATE TABLE Employees (EmployeeID INT PRIMARY KEY,EmployeeName NVARCHAR(100),EmployeeEmail NVARCHAR(100) );-- 为表添加描述 EXEC sp_addextendedproperty name NMS_Description, value N员工信息表, level0type NSchema, level0name dbo, level1type N…

栈和队列的经典例题,LeetCode 括号匹配问题;栈实现队列;队列实现栈;队列带环问题

1.前序 又有很久没有更新文章了&#xff0c;这次带你们手撕几道基础题&#xff1b;真的就和康纳吃饭一样简单&#xff01;&#xff01;&#xff01; 如果还不会队列和栈的可以去看看之前写的博客&#xff1b; 栈的实现 队列概念以及实现 <- 快速传送 目录 1.前序 …

优化css样式的网站

一、按钮的css样式 https://neumorphism.io/#e0e0e0https://neumorphism.io/#e0e0e0 二、渐变样式 Fresh Background Gradients | WebGradients.com &#x1f48e;Come to WebGradients.com for 180 beautiful linear gradients in CSS3, Photoshop and Sketch. This collect…

Redisson-分布式锁单Redis节点模式

Redisson-分布式锁单Redis节点模式 为什么要用分布式锁&#xff1f; 使用分布式锁的主要目的是为了解决多线程或多进程并发访问共享资源时可能出现的竞争条件和数据一致性问题。举一些实际场㬌&#xff1a; 数据库并发控制&#xff1a;在分布式系统中&#xff0c;多个节点同…