开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第6天,点击查看活动详情
- 232.用栈实现队列
-
- 用队列实现栈
栈与队列理论基础
栈和队列是STL(C++标准库)里面的两个数据结构。
栈(stack):先进后出。
- 就像装羽毛球的筒子一样,羽毛球依次放进去后,最先放进去的羽毛球要到最后才能拿到。
- 对栈的操作主要有两种,一是将一个元素压入栈,push方法,另一个就是将栈顶元素出栈,pop方法。
队列(queue):先进先出。
- 类似核酸排队,先来排队的人先做先走,后面来的人只能依次排队等候。
一些优秀的文章中详细叙述了相关理论知识。见参考文章
232.用栈实现队列
题目链接:232. 用栈实现队列 - 力扣(LeetCode)
请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(
push、pop、peek、empty):实现
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() 和 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)的栈,并支持普通栈的全部四种操作(
push、top、pop和empty)。实现
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() 操作。所以就用到了思路中的方法!