数据结构与算法【链表:一】Java实现
目录
链表
单向链表
哨兵链表
双向链表
环形链表
链表
链表是数据元素的线性集合,其每个元素都指向下一个元素,元素存储上并不连续。
随机访问性能
根据 index 查找,时间复杂度 O(n)
插入或删除性能
- 起始位置:O(1)
- 结束位置:如果已知 tail 尾节点是 O(1)[双向链表],不知道 tail 尾节点是 O(n)
- 中间位置:根据 index 查找时间 + O(1)
单向链表
单向链表中每个元素只知道下一个节点位置
单向链表的简单实现
public class SinglyLinkList implements Iterable<Integer> {private Node head = null;//节点类private static class Node {int value;Node next;public Node(int value, Node next) {this.value = value;this.next = next;}}//提供链表方法public void addFirst(int value) {head = new Node(value, head);}//向链表尾部添加节点public void addLast(int value) {Node last = findLast();if (last == null) {addFirst(value);}last.next = new Node(value, null);}private Node findLast() {if (head.next == null) {return null;}Node p = head;while (p.next != null) {p = p.next;}return p;}//遍历链表public void loop(Consumer<Integer> consumer) {Node pointer = head;while (pointer != null) {consumer.accept(pointer.value);pointer = pointer.next;}}@Overridepublic Iterator<Integer> iterator() {return new Iterator<Integer>() {Node pointer = head;@Overridepublic boolean hasNext() {return pointer != null;}@Overridepublic Integer next() {int value = pointer.value;pointer = pointer.next;return value;}};}// 根据索引查找节点值private Node findByIndex(int index) {int i = 0;for (Node p = head; p != null; p = p.next, i++) {if (index == i) {return p;}}return null;}public int get(int index) {Node node = findByIndex(index);if (node == null) {throw new IllegalArgumentException("索引超出范围");}return node.value;}//插入元素public void insert(int index, int value) {if (index == 0) {addFirst(value);return;}//查找索引的上一个节点Node node = findByIndex(index - 1);if (node == null) {throw new IllegalArgumentException("索引超出范围");}node.next = new Node(value, node.next);}//移除首节点public void removeFirst() {if (head==null){throw new RuntimeException("链表为空。无法移除");}head = head.next;}//删除指定节点public void remove(int index){if (index==0){removeFirst();}//找到要删除的节点的前驱节点Node node = findByIndex(index - 1);Node remove = node.next;if (remove==null){//要删除的节点位置为空throw new IllegalArgumentException("索引越界");}node.next = remove.next;}
}
这种实现方法有一个弊端,那就是每次添加或是删除元素时都需要进行判断链表是否为空。因此提出了哨兵链表的概念
哨兵链表
所谓哨兵列表,就是头节点不存储数据,只为简化边缘判断使用。
具体代码如下
public class SinglyLinkListSentinel implements Iterable<Integer> {//头节点指向哨兵节点。值可以随便给private Node head = new Node(723468, null);//节点类private static class Node {int value;Node next;public Node(int value, Node next) {this.value = value;this.next = next;}}public void addFirst(int value) {//在链表头部添加节点时,应该在哨兵之后。insert(0, value);}//向链表尾部添加节点public void addLast(int value) {Node last = findLast();last.next = new Node(value, null);}private Node findLast() {Node p = head;while (p.next != null) {p = p.next;}return p;}//遍历链表public void loop(Consumer<Integer> consumer) {//从哨兵节点之后的节点开始遍历Node pointer = head.next;while (pointer != null) {consumer.accept(pointer.value);pointer = pointer.next;}}@Overridepublic Iterator<Integer> iterator() {return new Iterator<Integer>() {Node pointer = head.next;@Overridepublic boolean hasNext() {return pointer != null;}@Overridepublic Integer next() {int value = pointer.value;pointer = pointer.next;return value;}};}// 根据索引查找节点值private Node findByIndex(int index) {//这里设置为-1是排除哨兵节点带来的影响int i = -1;for (Node p = head; p != null; p = p.next, i++) {if (index == i) {return p;}}return null;}public int get(int index) {Node node = findByIndex(index);if (node == null) {throw new IllegalArgumentException("索引超出范围");}return node.value;}//插入元素public void insert(int index, int value) {//查找索引的上一个节点Node node = findByIndex(index - 1);if (node == null) {throw new IllegalArgumentException("索引超出范围");}node.next = new Node(value, node.next);}//移除首节点public void removeFirst() {remove(0);}//删除指定节点public void remove(int index) {//找到要删除的节点的前驱节点Node node = findByIndex(index - 1);Node remove = node.next;if (remove == null) {//要删除的节点位置为空throw new IllegalArgumentException("索引越界");}node.next = remove.next;}
}
双向链表
每个元素知道上一个元素与下一个元素的地址
简单实现如下
public class DoublyLinkedListSentinel implements Iterable<Integer>{//头哨兵private Node head = new Node(null, null, 0);//尾哨兵private Node tail = new Node(null, null, 0);public DoublyLinkedListSentinel() {//初始化时,对头尾哨兵进行指定head.next = tail;tail.prev = head;}private static class Node {Node prev;Node next;int value;public Node(Node prev, Node next, int value) {this.prev = prev;this.next = next;this.value = value;}}//提供双向链表方法public Node findByIndex(int index) {int i = -1;for (Node p = head; p != tail; p = p.next, i++) {if (index == i) {return p;}}return null;}//插入首元素public void addFirst(int value) {insert(0, value);}//插入元素public void insert(int index, int value) {Node prev = findByIndex(index - 1);if (prev == null) {throw new RuntimeException("下标越界");}Node next = prev.next;Node node = new Node(prev, next, value);prev.next = node;next.prev = node;}//向尾节点添加public void addLast(int value){Node prev = tail.prev;Node addNode = new Node(prev, tail, value);prev.next = addNode;tail.prev = addNode;}//删除首节点public void removeFirst(){if (head.next==tail){throw new RuntimeException("链表为空");}Node remove = head.next;Node next = remove.next;head.next = next;next.prev = head;}//删除元素public void remove(int index) {Node prev = findByIndex(index - 1);if (prev == null) {throw new RuntimeException("下标越界");}Node remove = prev.next;Node next = remove.next;if (remove==tail){throw new RuntimeException("下标越界");}prev.next = next;next.prev = prev;}//获取元素public int get(int index){Node node = findByIndex(index);return node.value;}//遍历元素public void loop(Consumer<Integer> consumer) {Node pointer = head.next;while (pointer != tail) {consumer.accept(pointer.value);pointer = pointer.next;}}//迭代器实现@Overridepublic Iterator<Integer> iterator() {return new Iterator<Integer>() {Node p =head.next;@Overridepublic boolean hasNext() {return p!=tail;}@Overridepublic Integer next() {int value = p.value;p = p.next;return value;}};}
}
环形链表
环形链表的尾节点指向的是头节点head
环形链表我们也可以引入哨兵,空链表时,哨兵既做头节点也做尾节点。
简单实现代码如下
public class DoublyLinkedListSentinelTo implements Iterable<Integer> {private Node sentinel = new Node(null, null, -1);public DoublyLinkedListSentinelTo() {sentinel.prev = sentinel;sentinel.next = sentinel;}@Overridepublic Iterator<Integer> iterator() {return new Iterator<Integer>() {Node p = sentinel.next;@Overridepublic boolean hasNext() {return p != sentinel;}@Overridepublic Integer next() {int value = p.value;p = p.next;return value;}};}private static class Node {Node prev;Node next;int value;public Node(Node prev, Node next, int value) {this.prev = prev;this.next = next;this.value = value;}}//提供循环链表的方法public Node findByIndex(int index) {int i = 0;for (Node p = sentinel.next; p != sentinel; i++, p = p.next) {if (index == i) {return p;}}return null;}public void addFirst(int value) {Node next = sentinel.next;Node addNode = new Node(sentinel, next, value);sentinel.next = addNode;next.prev = addNode;}public void insert(int index, int value) {if (index == 0) {addFirst(value);}Node prev = findByIndex(index - 1);if (prev == null) {throw new RuntimeException("下标越界");}Node next = prev.next;Node insertNode = new Node(prev, next, value);next.prev = insertNode;prev.next = insertNode;}public void addLast(int value) {Node prev = sentinel.prev;Node addNode = new Node(prev, sentinel, value);prev.next = addNode;sentinel.prev = addNode;}//删除第一个节点public void removeFirst() {Node remove = sentinel.next;if (remove == sentinel) {throw new RuntimeException("下标越界");}Node next = remove.next;sentinel.next = next;next.prev = sentinel;}//删除最后一个节点public void removeLast() {Node remove = sentinel.prev;if (remove == sentinel) {throw new RuntimeException("下标越界");}Node prev = remove.prev;prev.next = sentinel;sentinel.prev = prev;}//删除指定删除节点public void remove(int index) {if (index == 0) {removeFirst();}Node prev = findByIndex(index - 1);Node remove = prev.next;if (remove == sentinel) {throw new RuntimeException("下标越界");}Node next = remove.next;prev.next = next;next.prev = prev;}//遍历节点public void loop(Consumer<Integer> consumer) {for (Node p = sentinel.next; p != sentinel; p = p.next) {consumer.accept(p.value);}}
}
相关文章:

数据结构与算法【链表:一】Java实现
目录 链表 单向链表 哨兵链表 双向链表 环形链表 链表 链表是数据元素的线性集合,其每个元素都指向下一个元素,元素存储上并不连续。 随机访问性能 根据 index 查找,时间复杂度 O(n) 插入或删除性能 起始位置:O(1)结束位…...

数据结构 | 队列的实现
数据结构 | 队列的实现 文章目录 数据结构 | 队列的实现队列的概念及结构队列的实现队列的实现头文件,需要实现的接口 Queue.h初始化队列队尾入队列【重点】队头出队列【重点】获取队列头部元素获取队列队尾元素获取队列中有效元素个数检测队列是否为空销毁队列 Que…...
flutter 集成 高德地图,退出界面闪退
android:allowNativeHeapPointerTagging"false"应用尝试释放系统堆分配器未分配的指针。 应用中的某个部分修改了指针的顶部字节。不能修改指针的顶部字节,您需要更改代码来修复此问题。 指针的顶部字节被错误使用或修改的示例包括: 指向特定…...

数据结构----链式栈的操作
链式栈的定义其实和链表的定义是一样的,只不过在进行链式栈的操作时要遵循栈的规则----即“先进后出”。 1.链式栈的定义 typedef struct StackNode {SElemType data;struct StackNode *next; }StackNode,*LinkStack; 2.链式栈的初始化 Status InitStack(LinkSta…...

识别伪装IP的网络攻击方法
识别伪装IP的网络攻击可以通过以下几种方法: 观察IP地址的异常现象。攻击者在使用伪装IP地址进行攻击时,往往会存在一些异常现象,如突然出现的未知IP地址、异常的流量等。这些现象可能是攻击的痕迹,需要对此加以留意。 检查网络通…...

C 语言指针
C 语言指针 在本教程中,您将学习指针。什么是指针,如何使用它们以及在示例的帮助下使用它们时可能遇到的常见错误。 指针是 C和C 编程的强大功能。在学习指针之前,让我们学习一下C语言编程中的地址。 C 语言地址 如果程序中有变量var&am…...

学【Java多态】-- 写高质量代码
多态的实现条件 在java中要实现,必须要满足如下几个条件,缺一不可。 1.必须在继承体系下2.子类必须要对父类中的方法进行重写3.通过父类的引用调用冲写的方法。 想要真正的学好多态需要去学习一些前置知识,那我们直接开始吧! …...

【汇编】内存的读写与地址空间、寄存器及数据存储
文章目录 前言一、CPU对存储器的读写1.1 cpu对存储器的读写如何进行?1.2 演示 二、内存地址空间三、将各类存储器看作一个逻辑存储器——统一编址内存地址空间的分配方案 三、CPU的组成寄存器是CPU内部的信息存储单元通用寄存器--AX为例“横看成岭侧成峰“ 四、“字…...

DSP生成hex方法
以下使用两种方法生成的HEX文件,亲测可用 (1)万能法 不管.out文件是哪个版本CCS编译器生成的,只要用HEX2000.exe软件,翻译都可以使用。方法: hex2000 -romwidth 16 -memwidth 16 -i -o 20170817chuankou…...

GZ038 物联网应用开发赛题第7套
2023年全国职业院校技能大赛 高职组 物联网应用开发 任 务 书 (第7套卷) 工位号:______________ 第一部分 竞赛须知 一、竞赛要求 1、正确使用工具,操作安全规范; 2、竞赛过程中如有异议,可向现场考评…...

ELK之Logstash解析时间相差8h的问题
一、问题描述 服务器当前时间为:2022年 06月 28日 星期二 11:24:22 CST 而logstash解析的时间为2022-06-28T03:15:25.545Z与实际时间相差8h 一、解决办法: 需改logstash的配置文件: 原理就是:定义一个中间变量timestamp&…...

uniapp+vite+vue3开发跨平台app,运行到安卓模拟器调试方法
因为没有使用hbuilder开发uniapp,而是使用了vscode和vite来开发的,所以怎么将这个程序运行到安卓模拟器调试开发呢?其实方法很简单,使用android studio创建一个模拟器或者其他mumu模拟器,然后将项目使用hbuilder打开&a…...

Ubuntu诞生已经19年了
导读2004 年 10 月 20 日,Ubuntu 4.10 正式发布,代号‘Warty Warthog’。 2004 年 10 月 20 日,Ubuntu 4.10 正式发布,代号‘Warty Warthog’。 ▲ Ubuntu 4.10 与最新版 Ubuntu 23.10 的对比 作为 Ubuntu 第一个版本࿰…...

跟着基金买,别墅靠大海?买基金重仓股票,会破产吗?| 附最新选股结果
2020年A股经历了一波结构性牛市。 抱团核心资产的公募基金历史性大赚2万亿,一跃成为全市场顶流。不仅常年霸榜热搜,甚至连游戏直播的弹幕都在讨论基金。 很多年轻人也纷纷跑步入场,毕竟支付宝买基金贼方便。 可惜好景不长,大盘急…...

【教3妹学编辑-mysql】mybatis查询条件遇到的坑及解决方案
2哥 :3妹,今天怎么下班这么晚啊。 3妹:嗨,别提了,今天线上出bug了, 排查了好久。 2哥:啊,什么问题呀? 3妹:我们内部的一个管理系统报错了, 最近排查下来是myb…...

032-从零搭建微服务-定时服务(一)
写在最前 如果这个项目让你有所收获,记得 Star 关注哦,这对我是非常不错的鼓励与支持。 源码地址(后端):mingyue: 🎉 基于 Spring Boot、Spring Cloud & Alibaba 的分布式微服务架构基础服务中心 源…...
精通Nginx(11)-缓存
缓存能够存储请求的响应结果,以供未来再次使用,进而加速内容的提供。内容缓存可以缓存完整的响应,减少上游服务器的负载,避免了每次都为相同的请求重新运行计算和查询的麻烦。缓存可以提高性能并减少负载,这意味着可以用更少的资源更快地提供服务。NGINX 允许在NGINX 服务…...

用excel计算矩阵的乘积
例如,我们要计算两个矩阵的乘积, 第一个矩阵是2*2的: 1234 第2个矩阵是2*3的: 5697810 在excel中鼠标点到其它空白的地方,用来存放矩阵相乘的结果: 选择插入-》函数: 选中MMULT,…...
【微软技术栈】C#.NET 中使用依赖注入
本文内容 先决条件创建新的控制台应用程序添加接口添加默认实现添加需要 DI 的服务为 DI 注册服务结束语 本文介绍如何在 .NET 中使用依赖注入 (DI)。 借助 Microsoft 扩展,可通过添加服务并在 IServiceCollection 中配置这些服务来管理 DI。 IHost 接口会公开 IS…...

开启学历新征程,电大搜题助您轻松获取知识
作为一名电大学者,有肩负着传递真实信息、宣传正面价值的使命,而今天我要向您介绍的是一款非常实用的学习工具——电大搜题微信公众号。通过该平台,您可以获得更多关于浙江开放大学和广播电视大学的学习资源,助您在学习和工作上取…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...

.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...
2024年赣州旅游投资集团社会招聘笔试真
2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...

React19源码系列之 事件插件系统
事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...
三体问题详解
从物理学角度,三体问题之所以不稳定,是因为三个天体在万有引力作用下相互作用,形成一个非线性耦合系统。我们可以从牛顿经典力学出发,列出具体的运动方程,并说明为何这个系统本质上是混沌的,无法得到一般解…...
Axios请求超时重发机制
Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式: 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...

【JavaWeb】Docker项目部署
引言 之前学习了Linux操作系统的常见命令,在Linux上安装软件,以及如何在Linux上部署一个单体项目,大多数同学都会有相同的感受,那就是麻烦。 核心体现在三点: 命令太多了,记不住 软件安装包名字复杂&…...

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列,以便知晓哪些列包含有价值的数据,…...

论文笔记——相干体技术在裂缝预测中的应用研究
目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术:基于互相关的相干体技术(Correlation)第二代相干体技术:基于相似的相干体技术(Semblance)基于多道相似的相干体…...
MySQL JOIN 表过多的优化思路
当 MySQL 查询涉及大量表 JOIN 时,性能会显著下降。以下是优化思路和简易实现方法: 一、核心优化思路 减少 JOIN 数量 数据冗余:添加必要的冗余字段(如订单表直接存储用户名)合并表:将频繁关联的小表合并成…...