【代码随想录 | day10】(JavaScript) 栈和队列系列:232.用栈实现队列、225. 用队列实现栈

91 阅读5分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第6天,点击查看活动详情

  • 232.用栈实现队列
    1. 用队列实现栈

栈与队列理论基础

栈和队列是STL(C++标准库)里面的两个数据结构。

栈(stack):先进后出

  • 就像装羽毛球的筒子一样,羽毛球依次放进去后,最先放进去的羽毛球要到最后才能拿到。
  • 对栈的操作主要有两种,一是将一个元素压入栈,push方法,另一个就是将栈顶元素出栈,pop方法。
image.png

队列(queue):先进先出

  • 类似核酸排队,先来排队的人先做先走,后面来的人只能依次排队等候。
image-20221127184437692.png

一些优秀的文章中详细叙述了相关理论知识。见参考文章


232.用栈实现队列

题目链接:232. 用栈实现队列 - 力扣(LeetCode)

请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(pushpoppeekempty):

实现 MyQueue 类:

  • void push(int x) 将元素 x 推到队列的末尾
  • int pop() 从队列的开头移除并返回元素
  • int peek() 返回队列开头的元素
  • boolean empty() 如果队列为空,返回 true ;否则,返回 false

思路

要定义两个栈,入栈(inStack)和出栈(outStack),用来模拟队列。

那么在JavaScript中怎么去定义这两个栈?可以用数组来模拟

  • 当调用 push() 方法时,只要把元素压入 inStack 即可:

  • 使用 peek() 或 pop() 操作队头的元素时:

    • 如果 outStack 为空,可以把 inStack 的所有元素取出再添加进 outStack,这时候 outStack 中元素就是先进先出顺序了。
    • 如果 outStack 不为空,直接从outStack 弹出数据就可以了。

补充知识点:JavaScript 中的常用Array方法

  • pop():删除数组的最后一个元素,并返回该元素。
  • push():将新元素添加到数组的末尾,并返回新的长度。
  • shift():删除数组的第一个元素,并返回该元素。
  • unshift():将新元素添加到数组的开头,并返回新的长度。

代码

在代码实现的时候,会发现pop() 和 peek()两个函数功能类似,代码实现上也是类似的,可以思考一下如何把代码抽象一下

 var MyQueue = function() {
   // 定义入栈和出栈
   this.inStack = [];
   this.OutStack = [];
 };

push —— 将元素 x 推到队列的末尾

 MyQueue.prototype.push = function(x) {
   this.inStack.push(x);
 };

pop —— 从队列的开头移除并返回元素

 MyQueue.prototype.pop = function () {
   let len = this.OutStack.length;
   // 如果出栈不为空
   if (len) {
     // 返回出栈中最后一个元素
     return this.OutStack.pop();
   }
   // 如果出栈如空,入栈的元素全部倒进来
   while (this.inStack.length) {
     this.OutStack.push(this.inStack.pop());
   }
   return this.OutStack.pop();
 };

peek —— 返回队列开头的元素

 MyQueue.prototype.peek = function() {
   let x = this.pop();
   this.OutStack.push(x);
   return x
 };
  • let x = this.pop(); 通过这一步,队列中删除了开头的元素,同时我们能拿到该元素
  • his.OutStack.push(x); 由于 peek 操作只是想返回开头的元素,不涉及到删除,所以上一步删除掉的元素,我们得放回原位置。

empty —— 判断队列是否为空

如果进栈和出栈都为空的话,说明模拟的队列为空了。

 MyQueue.prototype.empty = function() {
   return !this.inStack.length && !this.OutStack.length
 };

225.用队列实现栈

题目链接:225. 用队列实现栈 - 力扣(LeetCode)

请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(pushtoppopempty)。

实现 MyStack 类:

  • void push(int x) 将元素 x 压入栈顶。
  • int pop() 移除并返回栈顶元素。
  • int top() 返回栈顶元素。
  • boolean empty() 如果栈是空的,返回 true ;否则,返回 false

思路

队列模拟栈,其实一个队列就够了,那么我们先说一说两个队列来实现栈的思路。

1、两个队列来实现栈

队列是先进先出的规则,把一个队列中的数据导入另一个队列中,数据的顺序并没有变,并没有变成先进后出的顺序。

所以用栈实现队列, 和用队列实现栈的思路还是不一样的,这取决于这两个数据结构的性质。

双队列,两个队列来存储栈,主队列queue1 来表示 主队列,queue2 表示 辅助队列

把que1最后面的元素以外的元素都备份到que2,然后弹出最后面的元素,再把其他元素从que2导回que1。

当入栈操作时,先把入栈元素放入辅助队列,我们先将主队列内容导入辅助队列。此时主队列空,交换辅助队列和主队列

queue2中push进来一个数。如果queue1不为空,就把queue1给导入进来,this.queue2.push(this.queue1.shift());。最后把queue2变为queue1,[queue1,queue2] = [queue2,queue1]

 var MyStack = function() {
     this.queue1 = [];
     this.queue2 = [];
 };
 ​
 MyStack.prototype.push = function(x) {
     this.queue1.push(x);
 };
 ​
 MyStack.prototype.pop = function() {
     // 减少两个队列交换的次数, 只有当queue1为空时,交换两个队列
     if(!this.queue1.length) {  // 当队列1中没有元素的时候
         [this.queue1, this.queue2] = [this.queue2, this.queue1];
     }
     while(this.queue1.length > 1) {
         this.queue2.push(this.queue1.shift());
     }
     return this.queue1.shift();
 };
 ​
 MyStack.prototype.top = function() {
     const x = this.pop();
     this.queue1.push(x);
     return x;
 };
 ​
 MyStack.prototype.empty = function() {
     return !this.queue1.length && !this.queue2.length;
 };

具体的过程,对着代码画一画图,慢慢理一下就会发现其实不难!

2、一个队列实现栈

比如队列 [1, 2, 3],1 是最先进去的,那么事件上 1 先出,但是对于栈来说,是 3 先出来。怎么操作呢?从前面删除1,然后放到 3 后面,变成了 [2, 3, 1] 。继续从前面删除 2 ,然后放到 1 后面,就变成了 [3 , 1, 2] 。此时队列中最先弹出来的就是 3 。

 var MyStack = function() {
     this.queue = [];
 };
 ​
 MyStack.prototype.push = function(x) {
     this.queue.push(x);
 };
 ​
 MyStack.prototype.pop = function() {
     let size = this.queue.length;
     while(size-- > 1) {
         this.queue.push(this.queue.shift());
     }
     return this.queue.shift();
 };
 ​
 MyStack.prototype.top = function() {
     const x = this.pop();
     this.queue.push(x);
     return x;
 };
 ​
 MyStack.prototype.empty = function() {
     return !this.queue.length;
 };

注意有一点,可能有人想问:我都定义好了一个数组了,想用pop()移除并返回栈顶元素,那么相当于 [1, 2, 3] 里面返回 3 嘛,那为什么值直接用 Array.pop() 方法,直接删除了数组的最后一个元素并返回了?

实际上我们这一题是用数组来模拟队列,那么 123 按顺序入栈后,最先出来的只能是 1 。相对于数组来说 [1, 2, 3]想出栈只能先进行 shift() 操作。所以就用到了思路中的方法!


参考文章