JS 数据结构:队列

news2024/9/28 3:29:53

队列

定义: 队列(Queue) 是一种只在表的一端进行插入,而在另一端进行删除的数据结构。

  • 队头(front) 允许删除的一端,永远指向第一个元素前一个位置。
  • 队尾(rear) 允许插入的一端,永远是指向队列最后一个元素
  • 先进先出(First In First Out)的线性表,简称FIFO表
  • 空队列 当队列中没有元素

在这里插入图片描述

上溢和下溢

当队列满时再入队会产生空间溢出,简称“上溢”;当队列空时再出队也会产生溢出,简称“下溢”。上溢是一种出错状态,应该避免;下溢则是正常现象。
假上溢:

  • 因为在入队和出队的操作中,头尾指针只增加不减小,致使被删除元素的空间永远无法重新利用。因此,尽管队列中实际的元素个数远远小于向量空间的规模,但也可能由于尾指针巳超出向量空间的上界而不能做入队操作。该现象称为假上溢。
    在这里插入图片描述
    队尾指针为5,队头指针为1,此时尾指针已经指向了顺序队列最后一个元素,但可以看到0,1两个
    并没有被使用,此时就为假上溢。

顺序队列

定义: 队列的顺序存储结构简称顺序队列。

完整代码

基于 JS 数组实现,由于 JS 数组能够自动扩容,所以没有上溢问题。并且原生实现了队列方法。

class ArrayQueue {
  constructor() {
    this.queue = [];
  }
  // 入队
  enqueue(value) {
    return this.queue.push(value);
  }
  // 出队
  dequeue() {
    return this.queue.shift();
  }
  // 取队头元素
  peek() {
    return this.queue[0];
  }
  // 判断队列是否为空
  isEmpty() {
    return this.queue.length === 0;
  }
  // 取队列有多少个元素
  size() {
    return this.queue.length;
  }
  // 清空队列
  clear() {
    this.queue = [];
  }
}

基于 JS 对象实现。不存在上溢和假上溢问题。

class ObjectQueue {
  constructor() {
    this.queue = {};
    this.end = -1;
    this.front = -1;
  }
   // 入队
  enqueue(value) {
    if (this.front === -1) {
      this.front++;
    }
    this.queue[++this.end] = value;
  }
  // 出队
  dequeue() {
    if (!this.isEmpty()) {
      const res = this.queue[this.front];
      delete this.queue[this.front++];
      return res;
    }
    return null;
  }
  // 取队头元素
  peek() {
    if (!this.isEmpty()) {
      return this.queue[this.front];
    }
    return null;
  }
  // 判断队列是否为空
  isEmpty() {
    return this.front > this.end;
  }
  // 取队列有多少个元素
  size() {
    return this.end - this.front + 1;
  }
  // 清空队列
  clear() {
    this.queue = {};
    this.front = -1;
    this.end = -1;
  }
}

循环队列

为了解决假上溢问题,引入循环队列,即把向量空间想象为一个首尾相接的圆环,在循环队列中进行出队、入队操作时,头尾指针仍要加1,朝前移动。只不过当头尾指针指向向量上界(MAXNUM-1)时,其加1操作的结果是指向向量的下界0。

但是引进的循环队列又出现了新的问题,看图:在这里插入图片描述

判空和判满两种方法

即队空和队满的时都满足q->front==q->rear,我们无法利用这个条件判断队空还是队满!!!该怎么解决这个问题呢?有两种方法:

  1. 少用一个数据元素空间,以队尾指示器加 l等于队头指示器判断队满,即队满条件为:
    (q-> rear+l)%MAXNUM==q->front
  2. 使用一个计数器记录队列中元素的总数(实际上是队列长度)。

第一种方法实现

class LoopArrayQueue{
  constructor() {
    this.MAXNUM = 5;
    this.queue = new Array(this.MAXNUM);
    this.front = 0;
    this.rear = 0;
  }
  // 入队
  enqueue(value) {
    if ((this.rear + 1) % this.MAXNUM === this.front) {
      console.log('队列已满,入队失败');
      return;
    }
    this.rear++;
    this.queue[this.rear % this.MAXNUM] = value;
  }
  // 出队
  dequeue() {
  	if(!this.isEmpty()) {
    	this.front++;
    	const res = this.queue[this.front % this.MAXNUM];
    	this.queue[this.front % this.MAXNUM] = undefined;
    	return res;
    }
    return undefined;
  }
  // 取队头元素
  peek() {
    return this.queue[(this.front + 1) % this.MAXNUM];
  }
  // 判断队列是否为空
  isEmpty() {
    return this.front === this.rear;
  }
  // 取队列有多少个元素
  size() {
    return this.rear - this.front;
  }
  // 清空队列
  clear() {
    this.queue = new Array(this.MAXNUM);
    this.front = 0;
    this.rear = 0;
  }
}

第二种方法实现

class LoopArrayQueue{
  constructor() {
    this.MAXNUM = 5;
    this.queue = new Array(this.MAXNUM);
    this.front = -1;
    this.rear = -1;
    this.count = 0;
  }
  // 入队
  enqueue(value) {
    if (this.count === this.MAXNUM) {
      console.log('队列已满,入队失败');
      return;
    }
    this.count++;
    this.rear++;
    this.queue[this.rear % this.MAXNUM] = value;
  }
  // 出队
  dequeue() {
    if (this.count > 0){
      this.front++;
      this.count--;
      const res = this.queue[this.front % this.MAXNUM];
      this.queue[this.front % this.MAXNUM] = undefined;
      return res;
    }
    return undefined;
  }
  // 取队头元素
  peek() {
    return this.queue[(this.front + 1) % this.MAXNUM];
  }
  // 判断队列是否为空
  isEmpty() {
    return this.count === 0;
  }
  // 取队列有多少个元素
  size() {
    return this.count;
  }
  // 清空队列
  clear() {
    this.count = 0;
    this.front = -1;
    this.rear = -1;
    this.queue = new Array(this.MAXNUM);
  }
}

链队列

定义: 队列的链式存储结构简称为链队列,它是限制仅在表头删除和表尾插入的单链表。 显然仅有单链表的头指针不便于在表尾做插入操作,为此再增加一个尾指针,指向链表的最后一个结点。于是,一个链队列由头指针和尾指针唯一确定。
在这里插入图片描述

注意:虽然 JS 并没有指针的概念,但是对于对象这样的类型,对象的名字本质上就相当于指针。

虽然 JS 中数组实现了在队列的方法,但是它本质上还是数组操作,所以若从数组头部删除一个元素,那么在 JS 引擎内部还是需要移动后边的数组元素,很耗费性能。但是链式存储结构删除和添加元素复杂度为 O1。

节点存储结构:

class Node {
  constructor(value) {
    this.value = value;
    this.next = null;
  }
}

判断队空
注意:有 JS 本身并无指针概念,所以再实现“链队”时必须要定义一个变量 count 记录队列中元素个数,由 count == 0 作为判空条件

而其他语言(例如 c 语言)链队队空条件是pointer->front->next == NULL && pointer->rear->next == NULL而不是pointer->front == pointer->rear这是因为当队空的时候pointer->frontpointer->rear的值不一样。

原因: 如上图所示,当创建空链队时,队列头指针、尾指针都指向上图中黑色节点(这个黑色节点相当于顺序队列的-1,是一个初始节点),当链队列添加元素后尾指针会改变它指向的节点,此时pointer->rear的值是它指向的尾结点的地址。而当队列删除元素时,改变的是黑色节点的 next 指针(这样做是为了使头指针满足它的特点,具体看上边队列特点),这样pointer->front的值永远是黑色节点的地址。所以pointer->frontpointer->rear的值不一样,不能作为判断队空的条件。

出队列:

当使用其它语言(例如 c 语言)时此功能需要注意的就是当队列只剩一个元素时,即pointer->front->next == pointer->rear,而你要删除此元素,就需要先改变尾指针指向的节点,再删除,释放最后一个元素节点的内存。因为如果你不改变的话,则尾指针是指向最后一个元素节点的,而你删除释放最后一个元素节点的内存后,此时pointer->rear->next是一个野指针(野指针的危害就不用我说了吧!!!)。

完整代码

class LinkQueue {
  constructor() {
    const node = new Node();
    this.count = 0;
    this.head = node;
    this.rear = node;
  }
  // 入队
  enqueue(value) {
    const node = new Node(value);
    this.rear.next = node;
    this.rear = node;
    this.count++;
  }
  // 出队
  dequeue() {
    if (!this.isEmpty()) {
      const res = this.head.next.value;
      this.head.next = this.head.next.next;
      this.count--;
      if (this.isEmpty()) {
        this.rear = this.head;
      }
      return res;
    }
    return null;
  }
  // 取队头元素
  peek() {
    if (!this.isEmpty()) {
      return this.head.value;
    }
    return null;
  }
  // 判断是否为空
  isEmpty() {
    return this.count === 0;
  }
  // 返回队列元素个数
  size() {
    return this.count;
  }
  // 清空队列
  clear() {
    const node = new Node();
    this.count = 0;
    this.head = node;
    this.rear = node;
  }
}

双端队列

在这里插入图片描述

双端队列(deque)是一种允许我们同时从队列前端和后端添加和移除元素的特殊队列。即可以在队头入队、出队,也可在队尾入队、出队。

class ArrayDequeue {
  constructor() {
    this.queue = [];
  }
  // 从队头入队
  adFront(value) {
    this.queue.unshift(value);
  }
  // 从队尾入队
  addBack(value) {
    this.queue.push(value);
  }
  // 从队头出队
  removeFront() {
    return this.queue.shif();
  }
  // 从队尾出队
  removeBack() {
    return this.queue.pop();
  }
  // 返回队头元素
  removeFront() {
    return this.queue[0];
  }
  // 返回队尾元素
  removeBack() {
    return this.queue[this.queue.length - 1];
  }
  // 判断队列是否为空
  isEmpty() {
    return this.queue.length === 0;
  }
  // 取队列有多少个元素
  size() {
    return this.queue.length;
  }
  // 清空队列
  clear() {
    this.queue = [];
  }
}

相信经过栈和队列的学习,你已经感受到了 JS 数组的强大,其本身还具有很多方法,能够让我们更加方便的操控数组元素,可以看看这篇博客哦!总结 JS 数组及其方法

队列讲到这里就结束了。概念很好理解,重要的还是实现时候的细节需要仔细把控。我是孤城浪人,一名正在前端路上摸爬滚打的菜鸟,欢迎你的关注。

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

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

相关文章

一个漏测Bug能让你想到多少?

一、背景 漏测Bug是指产品逻辑缺陷在测试过程中没有被发现(尤其是测试环境可以重现的缺陷),上线版本发布后或者在用户使用体验后发现并反馈回来的缺陷。可能造成线上故障或者资损,在对产品测试过程中,自己也难免出现一…

大二Web课程设计期末考试——基于HTML+CSS+JavaScript+jQuery电商类化妆品购物商城

常见网页设计作业题材有 个人、 美食、 公司、 学校、 旅游、 电商、 宠物、 电器、 茶叶、 家居、 酒店、 舞蹈、 动漫、 服装、 体育、 化妆品、 物流、 环保、 书籍、 婚纱、 游戏、 节日、 戒烟、 电影、 摄影、 文化、 家乡、 鲜花、 礼品、 汽车、 其他等网页设计题目, A…

[C++] std::ranges中的特征和自定义std::ranges::view变换

文章目录1. std::ranges中的特征1.1. std::ranges::range例子细化1.2. std::ranges::sized_range1.3. std::ranges::borrowed_range1.4. std::ranges::view2. std::ranges::subrange 迭代器-哨位对2.1. 构造2.2. 结构化解绑2.3. 操作3. std::views中的std::ranges::view变换3.1…

如何安装 Elasticsearch

实战场景 如何安装 Elasticsearch 知识点 •CentOS •Java •Elasticsearch 安装 •Kibana 安装 菜鸟实战 Elasticsearch 是一个基于 Lucene 的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于 RESTful web 接口。Elasticsearch 是用 Java 语言开发的…

编译器设计(十一)——标量优化

文章目录一、简介二、消除无用和不可达代码2.1 消除无用代码2.2 消除无用控制流2.3 消除不可达代码三、代码移动3.1 缓式代码移动3.2 代码提升四、特化4.1 尾调用优化4.2 叶调用优化4.3 参数提升五、冗余消除5.1 值相同与名字相同5.2 基于支配者的值编号算法六、为其他变换制造…

ubuntu 创建桌面快捷启动

前言: ubuntu系统通常不会在桌面上生成启动图标,一般需要自己建一个下面提供常用的两个模板, 1.启动其他程序 2.启动文件快捷方式 一、创建其他程序的启动快捷图标 用pycharm2022为例子 touch pycharm2022.desktop gedit pycharm2022.d…

为什么 JVM 叫做基于栈的 RISC 虚拟机?

为什么 JVM 叫做基于栈的 RISC 虚拟机? 其实这个问题比较简单,今天这篇文章的主要目的是想让大家看一下分析这个问题的逻辑,并且如何更好地从一手资料里寻找这些问题的答案。 上图是《深入理解 Java 虚拟机》一书中的截图。其实,…

吊打面试官,HashMap 这一篇就够了

一、HashMap的简单使用 HashMap集合的创建&#xff1a; Map<String,String> map new HashMap<String,String>(); 使用put存储数据&#xff1a; map.put("张三","喜欢打游戏"); map.put("李四","喜欢睡觉"); map.put(…

电网运行信息检索系统的设计与实现

摘要 电网运行方式管理直接决定了电网企业的经济效益和安全效益,随着我国经济和社会的高速发展&#xff0c;我国电网的覆盖面积、网络节点和电压等级也高速增长。但是,我国当前电网运行方式管理工作水平还相对落后&#xff0c;制约了电网的安全经济效益。本文较为详细的分析了电…

第三章《数组与循环》第2节:多维数组

3.1小节介绍的数组都是把数组元素从逻辑上排成一行,因此它们被称为“一维数组”。如果一个班级内有15个学生,这些学生按照身高又分成3排就坐,其排列形式如图3-2所示: 3-2学生身高排列图 如果程序员希望按照每个学生在班级内的位置来存储他们的身高数据该怎么办呢?一些读者…

基于Java+Spring+Vue+elementUI大学生求职招聘系统详细设计实现

博主介绍&#xff1a;✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取联系&#x1f345;精彩专栏推荐订阅&#x1f447;&#x1f…

【项目设计】自主HTTP服务器

文章目录项目介绍网络协议栈介绍协议分层数据的封装与分用HTTP相关知识介绍HTTP的特点URL格式URI、URL、URNHTTP的协议格式HTTP的请求方法HTTP的状态码HTTP常见的HeaderCGI机制介绍CGI机制的概念CGI机制的实现步骤CGI机制的意义日志编写套接字相关代码编写HTTP服务器主体逻辑HT…

Windows AppData介绍

appdata是什么文件夹?&#xff08;应用程序数据&#xff09; 此文件夹是有关帐户信息、系统桌面、安装文件记录、快速启动文件夹等内容的。appdata下有三个子文件夹local&#xff0c;locallow&#xff0c;loaming。当你解压缩包时如果不指定路径&#xff0c;系统就把压缩包解到…

OpenCV图像处理——停车场车位识别

总目录 图像处理总目录←点击这里 十九、停车场车位识别 19.1、项目说明 唐宇迪老师的——OPENCV项目实战 学习 本项目的目的是设计一个停车场车位识别的系统&#xff0c;能够判断出当前停车场中哪些车位是空的。 任务共包含部分&#xff1a; 对图像预处理 从停车场的监控…

Vue基础之组件通信provide、inject

最近发现竟然还有小伙伴还不清楚provide、inject的用法&#xff0c;是时候普及一下provide、inject了&#xff1b; 常用的组件通信基本是父子组件通过props和emit来进行&#xff0c;一旦层级多了起来&#xff0c;props和emit就不好使了。每级都写props的话&#xff0c;会变得非…

PMP每日一练 | 考试不迷路-11.24(包含敏捷+多选)

11.27PMP考试倒计时 3天 每日5道PMP习题助大家上岸PMP&#xff01; ​题目1-2&#xff1a; ​1.在项目的规划阶段完成以后&#xff0c;但在正式执行之前&#xff0c;项目经理需要就项目目标进行沟通并获得承诺。项目经理下一步应该做什么? ( ) A.与所有相关方召开开工会议…

麻了,别再为难软件测试员了

前言 有不少技术友在测试群里讨论&#xff0c;近期的面试越来越难了&#xff0c;要背的八股文越来越多了,考察得越来越细&#xff0c;越来越底层&#xff0c;明摆着就是想让我们徒手造航母嘛&#xff01;实在是太为难我们这些测试工程师了。 这不&#xff0c;为了帮大家节约时…

深度操作系统20.5发布 deepin 20.5更新内容汇总

深度操作系统&#xff08;deepin&#xff09;是一款致力于为全球用户提供美观易用、安全稳定服务的Linux发行版&#xff0c;同时也一直是排名最高的来自中国团队研发的Linux发行版。深度操作系统20.5升级Stable内核至5.15.24&#xff0c;修复底层漏洞&#xff0c;进一步提升系统…

linux 清理垃圾文件

linux的文件系统比windows的要优秀&#xff0c;不会产生碎片&#xff0c;对于长时间运行的服务器来说尤为重要&#xff0c;而且linux系统本身也不会像windows一样产生大量的垃圾文件。不知道这个说法有没有可信度!至少我们可以确定的是linux系统的文件系统是比较优秀的! linux…

如何建立一个自己的网站?不懂代码搭建自己网站详细教程

搭建自己网站的准备&#xff1a; 1、首先需要注册购买一个域名&#xff0c;比如baidu.com&#xff0c;域名注册可以在阿里云或者其它域名注册平台注册。最常见的.com域名一般也就几十元一年&#xff1b; 域名 2、购买一个服务器&#xff0c;服务器也可以在阿里云或者景安等平…