在JavaScript中的数据结构(链表)

news2024/11/18 3:34:08

在这里插入图片描述

文章目录

  • 链表是什么?
  • 链表的好处
  • 详细的看一下列表
    • 单向链表
      • 实操链表
      • 向链表尾部追加元素
      • 从链表中移除元素
      • 根据元素的值移除元素
      • 在任意位置插入元素
      • 查找链表是否有改元素
      • 检查链表是否为空
      • 检查链表的长度
      • 查看链表头元素
      • 把LinkedList对象转换成一个字符串
      • 打印链表元素
    • 双向链表
    • 循环链表
        • 单向循环链表
        • 双向循环链表
  • 常用的操作链表函数
  • 总结


链表是什么?

JavaScript链表是一种数据结构,用于存储和组织一系列的元素。它由一系列节点(Node)组成,每个节点包含了两部分:数据域(存储数据)和指针域(指向下一个节点)。通过这种方式,链表中的节点可以按顺序链接在一起,形成一个链式结构。

与数组不同,链表的节点在内存中可以不连续存储,每个节点都可以独立分配内存,并通过指针连接到下一个节点,从而实现灵活的插入、删除操作。下图展示了一个链表的结构:
在这里插入图片描述
看图其实还是有点,一头雾水。用地铁举例吧,一列地铁是由一系列车厢组成的。每节车厢都相互连接。你很容易分离一节车厢,改变它的位置,添加或移除它。每节车皮都是列表的元素,车皮间的连接就是指针。


链表的好处

添加或移除元素的时候不需要移动其他元素,这是链表最大的好处。

存储多个元素,数组或列表是最常用的数据结构。每种语言都实现了数组,这种数据结构非常方便,提供了一个便利的[]语法来访问它的元素。然而,在大多数语言中这种数据结构有一个缺点:数组的大小是固定的,从数组的起点或中间插入或移除项的成本很高,因为需要移动元素。

链表存储有序的元素集合,但不同于数组,链表中的元素在内存中并不是连续放置的。每个元素由一个存储元素本身的节点和一个指向下一个元素的引用(也称指针或链接)组成。

链表可以灵活地插入、删除节点,不需要像数组一样进行扩容或拷贝操作。然而,链表的缺点是访问链表中的特定元素的时间复杂度较高,需要从头开始遍历链表直到找到目标节点。


详细的看一下列表

在JavaScript中,可以使用对象来实现链表。每个节点被表示为一个包含数据和指针属性的对象,通过这些对象之间的引用来构建链表结构。

常见的链表类型有单向链表(单链表),双向链表和循环链表。
以下逐一举例:

单向链表

每个节点只包含一个指向下一个节点的指针,最后一个节点的指针为空(null)。
在这里插入图片描述

实操链表

下面用实操一下链表,LinkedList类的骨架:

function LinkedList() { 
 let Node = function(element){
 //Node类表示要加入列表的项。它包含一个element属性,即要添加到列表的值,以及一个next属性,即指向列表中下一个节点项的指针。
  this.element = element; 
  this.next = null; 
 }; 
 let length = 0; //存储列表项的数量
 let head = null; //第一个节点的引用
 this.append = function(element){}; //向链表尾部追加元素
 this.insert = function(position, element){};//在任意位置插入元素
 this.removeAt = function(position){}; //从链表中移除元素
 this.remove = function(element){}; //根据元素的值移除元素
 this.indexOf = function(element){}; //查找链表是否有改元素
 this.isEmpty = function() {}; //检查链表是否为空
 this.size = function() {}; //检查链表的长度
 this.getHead = function(){}; //查看链表头元素
 this.toString = function(){}; //把LinkedList对象转换成一个字符串
 this.print = function(){}; //打印链表元素
} 

向链表尾部追加元素

向对象尾部添加一个元素时,可能有两种场景:列表为空,添加的是第一个元素,或者列表不为空,向其追加元素。
首先需要做的是把element作为值传入,创建Node项。
先来实现第一个场景:向为空的列表添加一个元素。当我们创建一个LinkedList对象时,
head会指向null:

this.append = function(element){ 
 let node = new Node(element), //{1} 
 current; // 指向列表中current项的变量
 if (head === null){ //列表中第一个节点 //如果head元素为null,要向列表添加第一个元素。
 head = node;
} else { 
 current = head; // 要向列表的尾部添加一个元素
 //循环列表,直到找到最后一项
 while(current.next){ 
 current = current.next; 
 } 
 //找到最后一项,将其next赋为node,建立链接
 current.next = node; //当前(也就是最后一个)元素的next指针指向想要添加到列表的节点 
 } 
 length++; //更新列表的长度 //{6} 
}; 

从链表中移除元素

现在,让我们看看如何从LinkedList对象中移除元素。移除元素也有两种场景:第一种是移
除第一个元素,第二种是移除第一个以外的任一元素。我们要实现两种remove方法:第一种是从
特定位置移除一个元素,第二种是根据元素的值移除元素(稍后我们会展示第二种remove方法)。

this.removeAt = function(position){ 
 //检查越界值
 if (position > -1 && position < length){ // 验证这个位置是有效
 let current = head, // 让head指向列表的第二个元素。用current变量创建一个对列表中第一个元素的引用
 previous,  
 index = 0;  
 //移除第一项
 if (position === 0){ //  如果不是有效的位置,就返回null,要从列表中移除第一个元素
 head = current.next; 
 } else { 
 while (index++ < position){ //  使用一个用于内部控制和递增的index变量
 previous = current; //  对当前元素的前一个元素的引用
 current = current.next; //  current变量总是为对所循环列表的当前元素的引用
 } 
 //将previous与current的下一项链接起来:跳过current,从而移除它
 previous.next = current.next; //  从列表中移除当前元素
 } 
 length--; 
 return current.element;
  } else { 
 return null; 
 } 
}; 

根据元素的值移除元素

需要先实现indexOf。indexOf在下面有讲述。

this.remove = function(element){ 
 let index = this.indexOf(element); 
 return this.removeAt(index); 
}; 

在任意位置插入元素

接下来,我们要实现insert方法。使用这个方法可以在任意位置插入一个元素。我们来看
一看它的实现:

this.insert = function(position, element){ 
 //检查越界值
 if (position >= 0 && position <= length){ //处理位置,需要检查越界值
 let node = new Node(element), 
 current = head, 
 previous, 
 index = 0; 
 if (position === 0){ //在第一个位置添加
 //现在要处理不同的场景。
 //第一种场景,需要在列表的起点添加一个元素,也就是第一个位置。
//current变量是对列表中第一个元素的引用。我们需要做的是把node.next的值设为
//current(列表中第一个元素)。现在head和node.next都指向了current。
//接下来要做的就是把head的引用改为node
 node.next = current; 
 head = node; 
 } else { 
 //现在来处理第二种场景:在列表中间或尾部添加一个元素。需要循环访问列表,
//找到目标位置
 while (index++ < position){ 
 previous = current; 
 current = current.next; 
 } 

 node.next = current; //跳出循环时,current变量对想要插入新元素的位置之后一个元素的引用
 //而previous将是对想要插入新元素的位置之前一个元素的引用。在这种情况下,我们要在previous和current之间添加新项。因此,首先需要把新项(node)和当前项链接起来
 previous.next = node; //然后需要改变previous和current之间的链接让previous.next指向node
 } 
 length++; //更新列表的长度
 return true; 
 } else { 
 return false; // 越界返回false,表示没有添加项到列表中
 } 
}; 

previous将是对列表最后一项的引用,而current将是null。在这种情况下,node.next将指向current,而previous.next将指向node,这样列表中就有了一个新的项。
现在来看看如何向列表中间添加一个新元素:
在这种情况下,试图将新的项(node)插入到previous和current元素之间。首先,需要把node.next的值指向current。然后把previous.next的值设为node。这样列表中就有了一个新的项。
使用变量引用需要控制的节点非常重要,这样就不会丢失节点之间的链接。可以只使用一个变量(previous),但那样会很难控制节点之间的链接。由于这个原因,最好是声明一个额外的变量来帮助处理这些引用。

查找链表是否有改元素

indexOf方法接收一个元素的值,如果在列表中找到它,就返回元素的位置,否则返回-1。

this.indexOf = function(element){ 
 let current = head, //循环访问列表变量初始值是head
 index = -1; //计算位置数
 while (current) {
 if (element === current.element) { 
 return index; //检查当前元素是否是要找的。如果是,就返回它的位置
 } 
 index++; // 就继续计数
 current = current.next; //检查列表中下一个节点
 } 
 return -1; 
}; 

如果列表为空,或是到达列表的尾部(current = current.next将是null),循环就不会执行。如果没有找到值,就返回-1。

检查链表是否为空

如果列表中没有元素,isEmpty方法就返回true,否则返回false。

this.isEmpty = function() { 
 return length === 0; 
}; 

检查链表的长度

this.size = function() { 
 return length; 
}; 

查看链表头元素

需要在类的实现外部循环访问列表,就需要提供一种获取类的第一个元素的方法。
head变量是LinkedList类的私有变量,只有通过LinkedList实例才可以,在外部被访问和
更改。

this.getHead = function(){ 
 return head; 
}; 

把LinkedList对象转换成一个字符串

toString方法会把LinkedList对象转换成一个字符串。下面是toString方法的实现:

this.toString = function(){ 
 let current = head, //要循环访问列表中的所有元素,就需要有一个起点,把current变量当作索引
 string = ''; //控制循环访问列表,初始化用于拼接元素值的变量
 while (current) { //循环访问列表中的每个元素
 string +=current.element +(current.next ? 'n' : '');//用current来检查元素是否存在
 //如果列表为空,或是到达列表中最后一个元素的下一位(null),while循环中的代码就不会执行
 //得到了元素的内容,将其拼接到字符串中
 current = current.next; //继续迭代下一个元素
 } 
 return string; // 返回列表内容的字符串
}; 

打印链表元素

为了检查元素,实现一个辅助方法print。把元素都输出到控制台:

this.print = function(){ 
 console.log(items.toString()); 
}; 

双向链表

每个节点除了包含指向下一个节点的指针外,还包含一个指向前一个节点的指针。这样,可以在需要的时候方便地进行双向遍历。

在这里插入图片描述


循环链表

循环链表可以像链表一样只有单向引用,也可以像双向链表一样有双向引用。循环链表和链表之间唯一的区别在于,最后一个元素指向下一个元素的指针(tail.next)不是引用null,而是指向第一个元素(head)。

单向循环链表

在这里插入图片描述

双向循环链表

在这里插入图片描述


常用的操作链表函数

  • append(element):向列表尾部添加一个新的项。
  • insert(position, element):向列表的特定位置插入一个新的项。
  • remove(element):从列表中移除一项。
  • indexOf(element):返回元素在列表中的索引。如果列表中没有该元素则返回-1。
  • removeAt(position):从列表的特定位置移除一项。
  • isEmpty():如果链表中不包含任何元素,返回true,如果链表长度大于0则返回false。
  • size():返回链表包含的元素个数。与数组的length属性类似。
  • toString():由于列表项使用了Node类,就需要重写继承自JavaScript对象默认的toString方法,让其只输出元素的值。

总结

链表是多个元素组成的列表,元素存储不连续,用next指针连接到一起,JS中没有链表,但是可以用Object模拟链表。

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

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

相关文章

盖雅「劳动力账户」:制造业全面工时成本管理利器

制造业是国民经济的重要支柱之一&#xff0c;也是国家实体经济发展的重点。随着国际竞争加剧&#xff0c;制造业企业需要不断优化和提升生产效率、降低成本&#xff0c;并保证产品的质量和可靠性。 在这过程中&#xff0c;制造业企业需要寻找新的技术与方法&#xff0c;挖掘生…

saltstack草稿

salt [options] <target> <module.function> [arguments] salt的自建函数&#xff1a; salt * test.rand_sleep 120 salt/salt/modules/test.py 这个是salt自带的包 salt * disk.usage salt -G ipv4:192.168.50.12 cmd.run ls -l /home salt * grain…

微服务开发系列 第九篇:OAuth2

总概 A、技术栈 开发语言&#xff1a;Java 1.8数据库&#xff1a;MySQL、Redis、MongoDB、Elasticsearch微服务框架&#xff1a;Spring Cloud Alibaba微服务网关&#xff1a;Spring Cloud Gateway服务注册和配置中心&#xff1a;Nacos分布式事务&#xff1a;Seata链路追踪框架…

CSS查缺补漏之《说一说CSS3有哪些新特性?》

面试时经常会被用到你了解CSS3新特性嘛&#xff0c;针对此问题&#xff0c;特整理如下~ 背景相关 background-size&#xff1a;用于设置背景图的尺寸 可选属性值有 length、percentage、cover、contain、auto length用长度值规定背景图片大小&#xff0c;若有两值&#xff0c;…

阿里云无影云电脑使用教程(3分钟新手指南)

​阿里云无影云电脑即无影云桌面&#xff0c;云桌面如何使用&#xff1f;云电脑创建后没有用户名和密码&#xff0c;先创建用户设置密码&#xff0c;才可以登录连接到云桌面。云桌面想要访问公网还需要开通互联网访问功能。阿里云百科来详细说下阿里云无影云电脑从选择、创建用…

项目管理,如何做到流程标准化?

项目管理如何做到刘春标准化&#xff1f;要想做好项目管理&#xff0c;可以借助于信息化工具&#xff0c;从以下方面入手&#xff1a; 1.明确目标 在项目管理中&#xff0c;确定团队的目标是非常重要的。团队需要制定一个清晰、可衡量的目标&#xff0c;以便能够全力以赴地实…

sqlserver------数据库的存储过程(练习)

对于数据库的存储过程之前的专题有讲过 这里具体讲述存储过程的编写方法&#xff1a; 例题&#xff1a;有heat表和eatables两张表&#xff0c;分别为&#xff1a; eatables heat&#xff1a;protein&#xff08;蛋白质&#xff09;&#xff0c;fat&#xff08;脂肪&#xff…

【计算机网络自顶向下】如何学好计网-第四章网络层

第四章 网络层 学习目的&#xff1a; 理解网络层服务的主要原理 网络岑服务模型转发&#xff08;forwarding&#xff09;和路由&#xff08;routing&#xff09;的概念对比路由器的工作原理路由算法及路由协议 完成简单的组网及IP地址和路由配置 4.1 引言 网络层提供的功能…

前端vue自定义简单实用下拉筛选 下拉菜单

前端vue自定义简单实用下拉筛选 下拉菜单, 下载完整代码请访问: https://ext.dcloud.net.cn/plugin?id13020 效果图如下: #### 使用方法 使用方法 <!-- titleArr: 选择项数组 dropArr: 下拉项数组 finishDropClick: 下拉筛选完成事件--> <ccDropDownMenu :titleA…

解密Prompt系列3. 冻结LM微调Prompt: Prefix-tuning Prompt-tuning P-tuning

这一章我们介绍在下游任务微调中固定LM参数&#xff0c;只微调Prompt的相关模型。这类模型的优势很直观就是微调的参数量小&#xff0c;能大幅降低LLM的微调参数量&#xff0c;是轻量级的微调替代品。和前两章微调LM和全部冻结的prompt模板相比&#xff0c;微调Prompt范式最大的…

21. 算法之动态规划

1. 概念 动态规划(Dynamic Programming)&#xff0c;是一种分阶段求解的方法。动态规划算法是通过拆分问题&#xff0c;定义问题状态和状态之间的关系&#xff0c;使得问题能够以递推&#xff08;或者说分治&#xff09; 的方式去解决。 首先是拆分问题&#xff0c;就是根据问…

生产报工软件怎么选?一定要看这几点,值得收藏!

生产报工软件怎么选&#xff1f; 适合项目型企业&#xff0c;支持移动端报工&#xff1b;可以进行工时上报、工时统计、人力成本核算&#xff1b;满足中大型企业需求。 题主的要求可以说非常具有代表性了&#xff0c;今天我们就来看一看如何寻找到这样的报工系统。 一、为什…

Alibaba Cloud Linux安装Nginx以及常用命令

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、安装C编译器&#xff0c;以及所需要的库二、下载和安装PCRE三.Ngxin相关命令 总结 前言 提示&#xff1a;系统是Alibaba Cloud Linux 3.2104 LTS 64位&…

批量更新状态功能在设备巡检、人员管理、工序流转等场景的应用

二维码已被应用在了各式各样的场景中&#xff0c;譬如教育培训、会议签到、产品展示等等。其中有很多场景需要一次性运用到大量的二维码&#xff0c;如人员管理、工序流转、设备巡检等&#xff0c;可以使用批量添加记录功能使工作效率近一步提升。 原先为一批二维码添加记录时…

dubbo3 Cluster wrapper初始化及extensionloader分析

从以下代码&#xff0c;可以看到dubbo默认的服务是failover SPI("failover") public interface Cluster {String DEFAULT "failover";Adaptive<T> Invoker<T> join(Directory<T> directory, boolean buildFilterChain) throws RpcExce…

达梦MPP集群搭建、DEM管理工具搭建MPP集群应用

说明... 3 两节点MPP集群手动搭建... 4 1、配置dm.ini 4 2、配置dmmal.ini 5 3、配置dmmpp.ctl 5 4、启动EP01和EP02数据库实例&#xff0c;系统搭建完成。... 6 MPP使用&#xff1a;... 6 使用DEM管理系统搭建MPP3节点集群... 10 一、达梦DEM部署... 11 1、创建DEM库…

计算机网络管理 常见的计算机网络管理工具snmputil,Mib browser,SNMPc管理软件的功能和异同

⬜⬜⬜ &#x1f430;&#x1f7e7;&#x1f7e8;&#x1f7e9;&#x1f7e6;&#x1f7ea;(*^▽^*)欢迎光临 &#x1f7e7;&#x1f7e8;&#x1f7e9;&#x1f7e6;&#x1f7ea;&#x1f430;⬜⬜⬜ ✏️write in front✏️ &#x1f4dd;个人主页&#xff1a;陈丹宇jmu &am…

【C++ 基础篇:25】:【重要模板】C++ 算术(赋值)运算符重载及自增自减运算符重载【以 Date 日期类为例】

系列文章说明 本系列 C 相关文章 仅为笔者学习笔记记录&#xff0c;用自己的理解记录学习&#xff01;C 学习系列将分为三个阶段&#xff1a;基础篇、STL 篇、高阶数据结构与算法篇&#xff0c;相关重点内容如下&#xff1a; 基础篇&#xff1a;类与对象&#xff08;涉及C的三大…

LabVIEW开发监测太阳能电池和损伤检测

LabVIEW开发监测太阳能电池和损伤检测 使用LabVEW监测太阳能电池的实时数据&#xff0c;利用LabVIEW实现太阳能跟踪和损伤检测。使用了太阳能电池板&#xff0c;Arduino UNO板&#xff0c;电压&#xff08;0-25V&#xff09;传感器LDR&#xff0c;温度传感器和伺服电机。Solar…

基于ubuntu20.4搭建的K8S集群新增工作节点带GPU显卡过程记录

基于ubuntu20.4搭建的K8S集群新增工作节点带GPU显卡过程记录 1、创建虚拟机引导选择efi 2、添加显卡,修改虚拟机-高级参数,添加以下两个参数 pciPassthru.64bitMMIOSizeGB:192 pciPassthru.use64bitMMIO:TRUE否则可能无法开机。 3、添加直通显卡,安装显卡驱动。 3.1、查…