JavaScript数据结构-模拟链表
在JavaScript中没有链表这种数据结构,但是我们可以用对象(Object)模拟链表,下面让我们先了解链表是什么。
链表(Linked List)是一种基础的数据结构,由一系列节点(Node)组成,每一个节点由两部分组成,分别是数据域和指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(即空指针的意思)。链表在插入和删除操作中具有较高的效率,广泛应用于实际开发中。
一、基本概念
链表是一种线性数据结构,它的每个元素都是一个节点。每个节点包含两部分:
- 数据域(Data):存储元素的值。
- 指针域(Next):存储指向下一个节点的引用。
二、链表的类型
1、单向链表(Singly Linked List)
- 描述:每个节点只包含指向下一个节点的指针。

2、双向链表(Doubly Linked List)
- 描述:每个节点包含指向前一个节点和下一个节点的指针。

3、循环链表(Circular Linked List)
- 描述:单向或双向链表的最后一个节点指向头节点,形成一个环。
单向循环链表

双向循环链表

二、模拟实现
1. 单向链表
下面是一个简单的单向链表的实现,包括定义节点,添加节点、删除节点、遍历链表等基本方法:
// 定义节点类
class ListNode {constructor(value) {this.value = value; // 节点的值this.next = null; // 指向下一个节点的指针,初始为 null}
}// 定义单向链表类
class LinkedList {constructor() {this.head = null; // 链表头节点,初始为 null}// 在链表末尾添加节点append(value) {// 创建一个新的节点const newNode = new ListNode(value);// 如果链表为空,新的节点作为头节点if (this.head === null) {this.head = newNode;} else {let current = this.head;// 遍历链表找到最后一个节点while (current.next !== null) {current = current.next;}// 将新的节点添加到最后一个节点的 nextcurrent.next = newNode;}}// 在链表头部添加节点prepend(value) {const newNode = new ListNode(value);newNode.next = this.head;this.head = newNode;}// 删除指定值的节点remove(value) {// 链表为空if (!this.head) {return null;}//如果要删除的节点是头节点if (this.head.value === value) {this.head = this.head.next;return;}let current = this.head;while (current.next && current.next.value !== value) {current = current.next;}// 如果找到了要删除的节点if (current.next) {current.next = current.next.next;}}// 遍历链表节点traverse() {let current = this.head;while (current !== null) {console.log(current.value);current = current.next;}}
}const list = new LinkedList();
list.append(1);
list.append(2);
list.append(3);
list.prepend(0);
list.traverse(); // 输出 0 1 2 3
2. 双向链表
下面是一个简单的双向链表的实现,包括定义节点,添加节点、删除节点、遍历链表等基本方法:
// 定义双向节点类
class DoublyListNode {constructor(value) {this.value = value; // 节点的值this.next = null; // 指向下一个节点的指针,初始为 nullthis.prev = null; // 指向前一个节点的指针,初始为 null}
}// 定义双向链表类
class DoublyLinkedList {constructor() {this.head = null; // 链表头节点,初始为 nullthis.tail = null; // 链表尾节点,初始为 null}// 在链表末尾添加节点append(value) {// 创建一个新的节点const newNode = new DoublyListNode(value);// 如果链表为空,新的节点作为头和尾节点if (this.tail === null) {this.head = newNode;this.tail = newNode;} else {// 将新的节点添加到尾节点的 nextthis.tail.next = newNode;// 新节点的 prev 指向当前的尾节点newNode.prev = this.tail;// 新节点作为新的尾节点this.tail = newNode;}}// 在链表头部添加节点prepend(value) {const newNode = new DoublyListNode(value);if (this.head === null) {this.head = newNode;this.tail = newNode;} else {// 头节点的 prev 指向新的节点this.head.prev = newNode;// 新节点的 next 指向当前的头节点newNode.next = this.head;// 新节点作为新的头节点this.head = newNode;}}// 删除指定值的节点remove(value) {if (this.head === null) return; // 如果链表为空,直接返回// 如果头节点就是要删除的节点if (this.head.value === value) {this.head = this.head.next;if (this.head !== null) {this.head.prev = null;} else {this.tail = null; // 如果链表为空,更新尾节点}return;}let current = this.head;while (current !== null) {if (current.value === value) { // 找到要删除的节点if (current.next !== null) {current.next.prev = current.prev;} else {this.tail = current.prev; // 更新尾节点}current.prev.next = current.next;return;}current = current.next;}}// 遍历链表节点traverse() {let current = this.head;while (current !== null) {console.log(current.value);current = current.next;}}
}// 示例:使用双向链表
const dList = new DoublyLinkedList();
dList.append(1);
dList.append(2);
dList.append(3);
dList.prepend(0);
dList.traverse(); // 输出 0 1 2 3
三、链表的进阶操作
1. 反转链表
思路:通过三个指针(prev、current和next)来遍历链表,逐个节点地改变其指向,最终实现链表的反转。具体来说,先将prev初始化为null,current指向头节点,然后在循环中不断更新这三个指针的值,直到current为null。
function reverseLinkedList(list) {let prev = null;let current = list.head;while (current !== null) {// 暂存下一个节点let next = current.next;// 将当前节点的 next 指向前一个节点current.next = prev; // 更新前一个节点为当前节点prev = current; // 继续遍历下一个节点current = next; }// 更新头节点为最后一个非空节点list.head = prev; return list;
}
2. 合并两个有序链表
思路:创建一个新的虚拟头节点,然后使用两个指针分别遍历两个有序链表,比较当前节点的值,将较小的节点连接到新链表的末尾,并移动相应的指针。重复此过程,直到其中一个链表为空,然后将另一个非空链表的剩余部分连接到新链表的末尾。
function mergeTwoLists(l1, l2) {// 创建一个哨兵节点let dummy = new ListNode(0); let current = dummy;let p1 = l1.head;let p2 = l2.head;// 遍历两个链表while (p1 !== null && p2 !== null) {if (p1.value < p2.value) {// 将较小值的节点添加到结果链表中current.next = p1; p1 = p1.next;} else {current.next = p2;p2 = p2.next;}current = current.next;}// 将剩余的节点连接到结果链表中if (p1 !== null) {current.next = p1;}if (p2 !== null) {current.next = p2;}let mergedList = new LinkedList();// 哨兵节点的 next 为合并后的头节点mergedList.head = dummy.next; return mergedList;
}
3. 查找中间节点
思路:使用两个指针的方法,一个指针每次移动一步,另一个指针每次移动两步。当快指针到达链表末尾时,慢指针刚好在中间节点。
function findMiddle(list){let slow = list.head;let fast = list.head;while (fast !== null && fast.next !== null) {slow = slow.next;fast = fast.next.next;}return slow;
}
四、总结
链表作为一种基础且重要的线性数据结构,在数据处理领域发挥着独特作用,尤其在应对频繁的插入与删除操作时展现出显著优势。深入了解并熟练掌握链表的基本操作以及更为复杂的进阶操作,能够为解决各类实际问题提供有力的支持。
相关文章:
JavaScript数据结构-模拟链表
在JavaScript中没有链表这种数据结构,但是我们可以用对象(Object)模拟链表,下面让我们先了解链表是什么。 链表(Linked List)是一种基础的数据结构,由一系列节点(Node)组成,每一个节…...
使用 Apache Jena 构建 RDF 数据处理与查询服务
一、引言 随着语义网和知识图谱技术的不断发展,RDF(Resource Description Framework)作为一种用于描述资源的框架,被广泛应用于知识表示和数据集成。Apache Jena 是一个功能强大的 Java 框架,用于处理 RDF 数据和 SPA…...
tableau之网络图和弧线图
一、网络图 概念 网络图(Network Graph),也称为网络可视化,是数据可视化的一种形式,用于显示实体(节点)之间的关系(边)。这种图表通过节点和边的结构揭示数据中的复杂关…...
el-date-picker 组件限制禁止选择当前时间之前的时间
页面代码 <el-date-pickerv-model"xxx.startTime"type"datetime"placeholder"请选择开始时间"value-format"YYYY-MM-DD HH:mm:ss"clearable:disabledDate"disabledDateFn":disabled-hours"disabledHours":dis…...
Linux网络数据包接收:原理、流程与优化策略
在当今数字化时代,网络已成为计算机系统不可或缺的部分。无论是日常的网页浏览、文件传输,还是大规模数据中心的高效通信,网络数据包的收发都在其中扮演着重要角色。对于 Linux 系统而言,深入理解网络数据包的接收过程,…...
django model.object.filter 不等于多个值
关于Django中QuerySet.filter()的使用问题。首先,我会分别针对“不等于多个值”的代码开发问题和可能遇到的报错问题给出解答。 代码开发问题:QuerySet.filter()不等于多个值 在Django中,如果你想在查询中排除多个值,可以使用__i…...
sklearn中的决策树-分类树:实例-分类树在合成数据集上的表现
分类树实例:分类树在合成数据集上的表现 代码分解 在不同结构的据集上测试一下决策树的效果(二分型,月亮形,环形) 导入 import numpy as np from matplotlib import pyplot as plt from matplotlib.colors import Li…...
给小米/红米手机root(工具基本为官方工具)——KernelSU篇
目录 前言准备工作下载刷机包xiaomirom下载刷机包【适用于MIUI和hyperOS】“hyper更新”微信小程序【只适用于hyperOS】 下载KernelSU刷机所需程序和驱动文件 开始刷机设置手机第一种刷机方式【KMI】推荐提取boot或init_boot分区 第二种刷机方式【GKI】不推荐 结语 前言 刷机需…...
棒球和垒球区别·棒球1号位
棒球运动和垒球运动的区别主要体现在以下几个方面: 1. 用球差异:垒球比棒球大且重。棒球的直径大约是7.3厘米,重量通常在145克左右,外皮由皮革制成,质地较硬。而垒球的直径为9.7厘米,重量大约为180克左右&a…...
Redis|持久化
文章目录 总体介绍RDB(Redis DataBase)官网介绍案例演示优势劣势如何检查修复 dump.rdb 文件哪些情况下会触发 RDB 快照如何禁用快照RDB 优化配置项详解小总结 AOF(Append Only File)官网介绍是什么能干嘛AOF 持久化工作流程AOF 缓…...
UE5销毁Actor,移动Actor,简单的空气墙的制作
1.销毁Actor 1.Actor中存在Destory()函数和Destoryed()函数 Destory()函数是成员函数,它会立即标记 Actor 为销毁状态,并且会从场景中移除该 Actor。它会触发生命周期中的销毁过程,调用 Destroy() 后,Actor 立即进入销毁过程。具体…...
蓝桥杯备赛-迷宫-BFS
这是一个关于二维迷宫的题目。我们要从迷宫的起点 S 走到终点 E,每一步我们只能选择上下左右四个方向中的一个前进一格。 W 代表墙壁,是不能进入的位置,除了墙壁以外的地方都可以走。迷宫内的 D 代表一道上锁的门,只有在持有钥匙的…...
设计模式 之 工厂模式(简单工厂模式、工厂方法模式、抽象工厂模式)(C++)
文章目录 C 工厂模式引言一、简单工厂模式概念实现步骤示例代码优缺点 二、工厂方法模式概念实现步骤示例代码优缺点 三、抽象工厂模式概念实现步骤示例代码优缺点 C 工厂模式 引言 在 C 编程中,对象的创建是一个常见且基础的操作。然而,当项目规模逐渐…...
Windows前端开发IDE选型全攻略
Windows前端开发IDE选型全攻略 一、核心IDE对比矩阵 工具名称最新版本核心优势适用场景推荐指数引用来源VS Code2.3.5轻量级/海量插件/跨平台/Git深度集成全栈开发/中小型项目⭐⭐⭐⭐⭐14WebStorm2025.1智能提示/框架深度支持/企业级调试工具大型项目/专业前端团队⭐⭐⭐⭐47…...
大模型训练中的数据不平衡问题及其解决策略
目录 大模型训练中的数据不平衡问题及其解决策略 一、数据不平衡问题的影响 二、处理数据不平衡问题的方法 1. 过采样(Oversampling) 2. 欠采样(Undersampling) 3. 代价敏感学习(Cost-Sensitive Learning…...
Flask应用实战经验总结:使用工厂函数创建app与uWSGI服务部署启动失败解决方案
在 Flask 应用开发中,使用工厂函数创建应用实例,并借助 uWSGI 服务进行部署,是常见且高效的组合。 然而,在实际操作过程中,uWSGI 配置文件与应用启动函数之间的关系复杂,容易引发各种问题。 本文将详细探…...
基于Spring Boot的党员学习交流平台设计与实现(LW+源码+讲解)
专注于大学生项目实战开发,讲解,毕业答疑辅导,欢迎高校老师/同行前辈交流合作✌。 技术范围:SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容:…...
TCP/IP的分层结构、各层的典型协议,以及与ISO七层模型的差别
1. TCP/IP的分层结构 TCP/IP模型是一个四层模型,主要用于网络通信的设计和实现。它的分层结构如下: (1) 应用层(Application Layer) 功能:提供应用程序之间的通信服务,处理特定的应用细节。 典型协议&am…...
【2025-02-25】基础算法:二分查找(一)
📝前言说明: ●本专栏主要记录本人的基础算法学习以及LeetCode刷题记录,主要跟随B站博主灵茶山的视频进行学习,专栏中的每一篇文章对应B站博主灵茶山的一个视频 ●题目主要为B站视频内涉及的题目以及B站视频中提到的“课后作业”。…...
WebRTC解析
一、WebRTC 协议概述 WebRTC(Web Real-Time Communication)是由 Google 发起并成为 W3C 标准的实时音视频通信技术,核心特点: 零插件:浏览器原生支持端到端加密(SRTP DTLS)P2P 优先架构&…...
BERT模型详解及代码复现
架构设计 BERT模型的架构设计是其成功的关键之一,它巧妙地融合了Transformer架构的优势,并针对自然语言处理任务进行了优化。具体来说,BERT的架构主要由三个模块组成: Embedding模块 :负责将输入的文本转换为模型可处理的向量表示。该模块由三种Embedding组成: Token Em…...
如何在 SpringBoot 项目使用 Redis 的 Pipeline 功能
本文是博主在批量存储聊天中用户状态和登陆信息到 Redis 缓存中时,使用到了 Pipeline 功能,并对此做出了整理。 一、Redis Pipeline 是什么 Redis 的 Pipeline 功能可以显著提升 Redis 操作的性能,性能提升的原因在于可以批量执行命令。当我…...
Python Django系列—入门实例
我们假定你已经阅读了 安装 Django。你能知道 Django 已被安装,且安装的是哪个版本,通过在命令提示行输入命令(由 $ 前缀)。 $ python -m django --version 如果这行命令输出了一个版本号,证明你已经安装了此版本的…...
2024年第十五届蓝桥杯青少 图形化编程(Scratch)省赛中级组真题——截取递增数
截取递增数 背景信息 递增数:如果一个大于9的正整数各个数位上的数,从左到右是逐渐变大的,那么就称这个数为递增数。 例如124、248 是递增数。 给你一个不含0的九位数,请找出从这个九位数中能截取出的所有递增数。例如:115367…...
【ECMAScript6】
【ECMAScript6】 01. ES6介绍02. let和const命令03. 模板字符串04. 函数之默认值、剩余参数05. 函数之扩展运算符、箭头函数06. 箭头函数this指向和注意事项07. 解构赋值08. 扩展的对象的功能(简写)09. Symbol类型10. Set集合数据类型11. Map数据类型12.…...
WebUI 部署 Ollama 可视化对话界面
文章目录 一、Node.js 安装1.系统环境查询2.官网下载nodejs 安装包3.安装 Node.js 并配置环境变量4.验证安装是否正确 二、ollama-webui 安装与配置1.代码库下载2.依赖安装3.运行 三、遇到问题与解决 一、Node.js 安装 1.系统环境查询 ubuntu20.04 系统,x86-64架构…...
BMS应用软件开发 — 17 上下电控制与诊断开发 (Simulink)
目录 17.1 上下电控制流程 17.1.1 上下电流程 17.1.2 下电过程的电机放电 17.1.3 继电器状态检测 17.2 预充继电器状态判断 17.1 上下电控制流程 17.1.1 上下电流程 高压上电是指动力电池为车辆提供高压,使高压回路导通,为车辆的各个高压部件供电&…...
UE5 Gameplay框架及继承关系详解
文章目录 前言一、核心类及其继承关系二、核心类的职责与协作2.1 Actor & Pawn2.2 Controller2.3 GameMode & GameState2.4 PlayerState2.5 HUD & UI 三、协作流程示例总结 前言 Unreal Engine 5(UE5)的 Gameplay 框架 是一个高度模块化的系…...
html - 手工添加上次阅读的位置, 方便下次阅读
文章目录 html - 手工添加上次阅读的位置, 方便下次阅读概述笔记END html - 手工添加上次阅读的位置, 方便下次阅读 概述 在看一本电子书,有pdf格式的,但是比较喜欢看html格式的(复制比较方便)。 但是有个缺点,如果看到一半,关掉…...
JavaSE学习笔记26-集合(Collection)
集合 Java 中的集合(Collection)是 Java 标准库中非常重要的一部分,用于存储和操作一组对象。Java 集合框架(Java Collections Framework)提供了一套丰富的接口和类,用于处理各种数据结构,如列…...
