JavaScript【栈和队列】
创始人
2025-05-31 11:08:35

目录

一、栈结构(Stack)

1.简介

​编辑

程序中的栈结构:

2.栈常见的操作:

1.封装栈类

2.栈结构的简单应用:

二、队列结构(Queue)

1.队列简介

队列的应用:

队列类的实现:

队列的常见操作:

2.封装队列类

代码实现

测试代码

3.队列的应用

代码实现

三、优先队列

1.优先级队列的实现

代码实现:

测试代码:

2.数组splice用法

3.数组的push


一、栈结构(Stack)

1.简介

数组是一个线性结构,并且可以在数组的任意位置插入和删除元素。而栈和队列就是比较常见的受限的线性结构。如下图所示:

栈的特点为先进后出,后进先出(LIFO:last in first out)。

程序中的栈结构:

  • 函数调用栈:A(B(C(D()))):即A函数中调用B,B调用C,C调用D;在A执行的过程中会将A压入栈,随后B执行时B也被压入栈,函数C和D执行时也会被压入栈。所以当前栈的顺序为:A->B->C->D(栈顶);函数D执行完之后,会弹出栈被释放,弹出栈的顺序为D->C->B->A;

  • 递归:为什么没有停止条件的递归会造成栈溢出?比如函数A为递归函数,不断地调用自己(因为函数还没有执行完,不会把函数弹出栈),不停地把相同的函数A压入栈,最后造成栈溢出(Stack Overfloat)

3.练习:题目:有6个元素6,5,4,3,2,1按顺序进栈,问下列哪一个不是合法的出栈顺序?

  • A:5 4 3 6 1 2 (√)
  • B:4 5 3 2 1 6 (√)
  • C:3 4 6 5 2 1 (×)
  • D:2 3 4 1 5 6 (√)

题目所说的按顺序进栈指的不是一次性全部进栈,而是有进有出,进栈顺序为6 -> 5 -> 4 -> 3 -> 2 -> 1。

解析:

  • A答案:65进栈,5出栈,4进栈出栈,3进栈出栈,6出栈,21进栈,1出栈,2出栈(整体入栈顺序符合654321);
  • B答案:654进栈,4出栈,5出栈,3进栈出栈,2进栈出栈,1进栈出栈,6出栈(整体的入栈顺序符合654321);
  • C答案:6543进栈,3出栈,4出栈,之后应该5出栈而不是6,所以错误;
  • D答案:65432进栈,2出栈,3出栈,4出栈,1进栈出栈,5出栈,6出栈。符合入栈顺序; 

2.栈常见的操作:

  • push(element):添加一个新元素到栈顶位置;
  • pop():移除栈顶的元素,同时返回被移除的元素;
  • peek():返回栈顶的元素,不对栈做任何修改(该方法不会移除栈顶的元素,仅仅返回它);
  • isEmpty():如果栈里没有任何元素就返回true,否则返回false;
  • size():返回栈里的元素个数。这个方法和数组的length属性类似;
  • toString():将栈结构的内容以字符串的形式返回。

1.封装栈类

代码实现

    // 封装栈类function Stack(){// 栈中的属性this.items =[]// 栈的相关操作// 1.push():将元素压入栈//方式一(不推荐):给对象添加方法,其他对象不能复用// this.push = () => {// }//方式二(推荐):给Stack类添加方法,能够多对象复用Stack.prototype.push = function(element) {// 利用数组item的push方法实现Stack类的pop方法this.items.push(element)}// 2.pop():从栈中取出元素Stack.prototype.pop = () => {// 利用数组item的pop方法实现Stack类的pop方法return this.items.pop()}// 3.peek():查看一下栈顶元素Stack.prototype.peek = () => {return this.items[this.items.length - 1]}// 4.isEmpty():判断栈是否为空Stack.prototype.isEmpty = () => {// 两个小时的教训啊不是this.length(不是Stack对象的length,Stack类没有length属性啊),而是			Stack类中定义的数组items才有length属性呀return this.items.length == 0 }// 5.size():获取栈中元素的个数Stack.prototype.size = () => {return this.items.length}// 6.toString():以字符串形式输出栈内数据Stack.prototype.toString = () => {//希望输出的形式:20 10 12 8 7let resultString = ''for (let i of this.items){resultString += i + ' '}return resultString}}

测试代码:

 // 栈的使用let  s = new Stack()s.push(20)s.push(10)s.push(100)s.push(77)console.log(s)													//65console.log(s.pop());											//68console.log(s.pop());											//69console.log(s.peek());											//71console.log(s.isEmpty());										//72console.log(s.size());											//74console.log(s.toString());										//75

2.栈结构的简单应用:

利用栈结构的特点封装十进至转换为二进至的函数:

 

 

    //简单应用://封装函数:将十进制转成二进制(十转二的运算最后倒叙取余的特点符合栈'先进后出')let dec2bin = decNumber => {//1.定义一个栈对象,保存余数var  stack = new Stack()// 2.循环操作while(decNumber > 0){// 2.1.获取余数并放入栈中stack.push(decNumber % 2)// 2.2.获取整除后的结果作为下一次运算的数字(floor:向下取整)decNumber = Math.floor(decNumber / 2)}// 3.从栈中取出0和1let  binaryString = '';let a = stack.items.lengthwhile(stack.items.length != 0){binaryString += stack.pop();}return binaryString;}//测试代码console.log(dec2bin(10));										//103console.log(dec2bin(100));										//104console.log(dec2bin(1000));										//105

二、队列结构(Queue)

1.队列简介

队列是是一种受限的线性表,特点为先进先出(FIFO:first in first out)

  • 受限之处在于它只允许在表的前端(front)进行删除操作;
  • 在表的后端(rear)进行插入操作;

相当于排队买票,先来的先买票,后来的后买票。

队列的应用:

  • 打印队列:计算机打印多个文件的时候,需要排队打印;
  • 线程队列:当开启多线程时,当新开启的线程所需的资源不足时就先放入线程队列,等待CPU处理;

队列类的实现:

队列的实现和栈一样,有两种方案:

  • 基于数组实现;
  • 基于链表实现;

队列的常见操作:

  • enqueue(element):向队列尾部添加一个(或多个)新的项;
  • dequeue():移除队列的第一(即排在队列最前面的)项,并返回被移除的元素;
  • front():返回队列中的第一个元素——最先被添加,也将是最先被移除的元素。队列不做任何变动(不移除元素,只返回元素信息与Stack类的peek方法非常类似);
  • isEmpty():如果队列中不包含任何元素,返回true,否则返回false;
  • size():返回队列包含的元素个数,与数组的length属性类似;
  • toString():将队列中的内容,转成字符串形式;

2.封装队列类

代码实现

    // 基于数组封装队列类function Queue() {// 属性this.items = []// 方法// 1.enqueue():将元素加入到队列中Queue.prototype.enqueue = element => {this.items.push(element)}// 2.dequeue():从队列中删除前端元素Queue.prototype.dequeue = () => {return this.items.shift()}// 3.front():查看前端的元素Queue.prototype.front = () => {return this.items[0]}// 4.isEmpty:查看队列是否为空Queue.prototype.isEmpty = () => {return this.items.length == 0;}// 5.size():查看队列中元素的个数Queue.prototype.size = () => {return this.items.length}// 6.toString():将队列中元素以字符串形式输出Queue.prototype.toString = () => {let resultString = ''for (let i of this.items){resultString += i + ' '}return resultString}}

测试代码

 	// 创建队列let queue = new  Queue()// 将元素加入到队列中queue.enqueue('a')queue.enqueue('b')queue.enqueue('c')queue.enqueue('d')console.log(queue);												//58// 从队列中删除元素queue.dequeue()console.log(queue);												//62queue.dequeue()console.log(queue);												//64//frontconsole.log(queue.front());								 		//67// 验证其他方法console.log(queue.isEmpty());								 	//70console.log(queue.size());								 		//71console.log(queue.toString());								 	//72

3.队列的应用

使用队列实现小游戏:击鼓传花,传入一组数据和设定的数字num,循环遍历数组内元素,遍历到的元素为指定数字num时将该元素删除,直至数组剩下一个元素。

代码实现

    // 队列应用:面试题:击鼓传花let passGame = (nameList, num) => {//1.创建队列结构let queue = new Queue()//2.将所有人依次加入队列// 这是ES6的for循环写法,i相当于nameList[i]for (let i of nameList) {queue.enqueue(i)}// 3.开始数数while (queue.size() > 1) {//队列中只剩1个人就停止数数// 不是num的时候,重新加入队列末尾// 是num的时候,将其从队列中删除// 3.1.num数字之前的人重新放入队列的末尾(把队列前面删除的加到队列最后)for (let i = 0; i < num - 1; i++) {// queue.dequeue()将从队列前面取出来的数据放入队列尾部queue.enqueue(queue.dequeue())}// 3.2.num对应这个人,直接从队列中删除/*思路是这样的,由于队列没有像数组一样的下标值不能直接取到某一元素,所以采用,把num前面的num-1个元素先删除后添加到队列末尾,这样第num个元素就排到了队列的最前面,可以直接使用dequeue方法进行删除*/queue.dequeue()}//4.获取剩下的那个人console.log(queue.size());									//104let endName = queue.front()console.log('最终剩下的人:' + endName);						   //106	return nameList.indexOf(endName);}//5.测试击鼓传花let names = ['lily', 'lucy', 'Tom', 'Lilei', 'Tony']console.log(passGame(names, 3));			

三、优先队列

优先级队列主要考虑的问题为:

  • 每个元素不再只是一个数据,还包含数据的优先级;
  • 在添加数据过程中,根据优先级放入到正确位置;

1.优先级队列的实现

代码实现:

    // 封装优先级队列function PriorityQueue() {//内部类:在类里面再封装一个类;表示带优先级的数据function QueueElement(element, priority) {this.element = element;this.priority = priority;} // 封装属性this.items = []// 1.实现按照优先级插入方法PriorityQueue.prototype.enqueue = (element, priority) => {// 1.1.创建QueueElement对象let queueElement = new QueueElement(element, priority)// 1.2.判断队列是否为空if(this.items.length == 0){this.items.push(queueElement)}else{// 定义一个变量记录是否成功添加了新元素let added = falsefor(let i of this.items){// 让新插入的元素与原有元素进行优先级比较(priority越小,优先级越大)if(queueElement.priority < i.priority){this.items.splice(i, 0, queueElement)added = true// 新元素已经找到插入位置了可以使用break停止循环break}}// 新元素没有成功插入,就把它放在队列的最前面if(!added){this.items.push(queueElement)}}}// 2.dequeue():从队列中删除前端元素PriorityQueue.prototype.dequeue = () => {return this.items.shift()}// 3.front():查看前端的元素PriorityQueue.prototype.front = () => {return this.items[0]}// 4.isEmpty():查看队列是否为空PriorityQueue.prototype.isEmpty = () => {return this.items.length == 0;}// 5.size():查看队列中元素的个数PriorityQueue.prototype.size = () => {return this.items.length}// 6.toString():以字符串形式输出队列中的元素PriorityQueue.prototype.toString = () => {let resultString = ''for (let i of this.items){resultString += i.element + '-' + i.priority + ' '}return resultString}}

测试代码:

    // 测试代码let pq = new PriorityQueue();pq.enqueue('Tom',111);pq.enqueue('Hellen',200);pq.enqueue('Mary',30);pq.enqueue('Gogo',27);// 打印修改过后的优先队列对象console.log(pq);

2.数组splice用法

  • splice(1,0,'Tom'):表示在索引为1的元素前面插入元素’Tom‘(也可以理解为从索引为1的元素开始删除,删除0个元素,再在索引为1的元素前面添加元素'Tom');

  • splice(1,1,'Tom'):表示从索引为1的元素开始删除(包括索引为1的元素),共删除1个元素,并添加元素'Tom'。即把索引为1的元素替换为元素'Tom'。

3.数组的push

  • 数组:在数组[0,1,2]中,pop(3),结果为[0,1,2,3];
  • :执行pop(0),pop(1),pop(2),pop(3),从栈底到栈顶的元素分别为:0,1,2,3;如果看成数组,可写为[0,1,2,3],但是索引为3的元素3其实是栈顶元素;所以说栈的push方法是向栈顶添加元素(但在数组的视角下为向数组尾部添加元素);
  • 队列:enqueue方法可以由数组的push方法实现,与数组相同,相当于在数组尾部添加元素。

可以这样想:栈结构是头朝下(索引值由下往上增大)的数组结构。

 

相关内容

热门资讯

Linux学习之端口、网络协议... 端口:设备与外界通讯交流的出口 网络协议:   网络协议是指计算机通信网...
kuernetes 资源对象分... 文章目录1. pod 状态1.1 容器启动错误类型1.2 ImagePullBackOff 错误1....
STM32实战项目-数码管 程序实现功能: 1、上电后,数码管间隔50ms计数; 2、...
TM1638和TM1639差异... TM1638和TM1639差异说明 ✨本文不涉及具体的单片机代码驱动内容,值针对芯...
Qt+MySql开发笔记:Qt... 若该文为原创文章,转载请注明原文出处 本文章博客地址:https://h...
Java内存模型中的happe... 第29讲 | Java内存模型中的happen-before是什么? Java 语言...
《扬帆优配》算力概念股大爆发,... 3月22日,9股封单金额超亿元,工业富联、鸿博股份、鹏鼎控股分别为3.0...
CF1763D Valid B... CF1763D Valid Bitonic Permutations 题目大意 拱形排列࿰...
SQL语法 DDL、DML、D... 文章目录1 SQL通用语法2 SQL分类3 DDL 数据定义语言3.1 数据库操作3.2 表操作3....
文心一言 VS ChatGPT... 3月16号,百度正式发布了『文心一言』,这是国内公司第一次发布类Chat...
CentOS8提高篇5:磁盘分...        首先需要在虚拟机中模拟添加一块新的硬盘设备,然后进行分区、格式化、挂载等...
Linux防火墙——SNAT、... 目录 NAT 一、SNAT策略及作用 1、概述 SNAT应用环境 SNAT原理 SNAT转换前提条...
部署+使用集群的算力跑CPU密... 我先在开头做一个总结,表达我最终要做的事情和最终环境是如何的,然后我会一...
Uploadifive 批量文... Uploadifive 批量文件上传_uploadifive 多个上传按钮_asing1elife的...
C++入门语法基础 文章目录:1. 什么是C++2. 命名空间2.1 域的概念2.2 命名...
2023年全国DAMA-CDG... DAMA认证为数据管理专业人士提供职业目标晋升规划,彰显了职业发展里程碑及发展阶梯定义...
php实现助记词转TRX,ET... TRX助记词转地址网上都是Java,js或其他语言开发的示例,一个简单的...
【分割数据集操作集锦】毕设记录 1. 按要求将CSV文件转成json文件 有时候一些网络模型的源码会有data.json这样的文件里...
Postman接口测试之断言 如果你看文字部分还是不太理解的话,可以看看这个视频,详细介绍postma...
前端学习第三阶段-第4章 jQ... 4-1 jQuery介绍及常用API导读 01-jQuery入门导读 02-JavaScri...
4、linux初级——Linu... 目录 一、用CRT连接开发板 1、安装CRT调试工具 2、连接开发板 3、开机后ctrl+c...
Urban Radiance ... Urban Radiance Fields:城市辐射场 摘要:这项工作的目标是根据扫描...
天干地支(Java) 题目描述 古代中国使用天干地支来记录当前的年份。 天干一共有十个,分别为:...
SpringBoot雪花ID长... Long类型精度丢失 最近项目中使用雪花ID作为主键,雪花ID是19位Long类型数...
对JSP文件的理解 JSP是java程序。(JSP本质还是一个Servlet) JSP是&#...
【03173】2021年4月高... 一、单向填空题1、大量应用软件开发工具,开始于A、20世纪70年代B、20世纪 80年...
LeetCode5.最长回文子... 目录题目链接题目分析解题思路暴力中心向两边拓展搜索 题目链接 链接 题目分析 简单来说࿰...
unity的C#学习——浮点常... 浮点常量 在C#中,一个浮点常量是由整数部分、小数点、小数部分和指数部分组成。浮点常量...
Angular 开发NPM第三... 准备工作 首先已经安装过node以及angular以及注册过npm账号 新建项目 ng new ...
【Linux Manpage】... NAME libi2c - publicly accessible functions provid...