跳到主要内容

普通队列

队列实现栈


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

实现 MyStack 类:

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

示例1:

输入:
["MyStack", "push", "push", "top", "pop", "empty"]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 2, 2, false]

解释:
MyStack myStack = new MyStack();
myStack.push(1);
myStack.push(2);
myStack.top(); // 返回 2
myStack.pop(); // 返回 2

进阶: 你能否实现每种操作的均摊时间复杂度为 O(1) 的栈?换句话说,执行 n 个操作的总时间复杂度 O(n) ,尽管其中某个操作可能需要比其他操作更长的时间。你可以使用两个以上的队列。

方案A 两个队列

思路: 使用两个队列实现栈的操作,其中 queue1用于存储栈内的元素,queue2 作为入栈操作的辅助队列。

  • 入栈操作时,首先将元素入队到 queue1
  • 将 quque1 的全部元素依次出队并入队到
  • 此时 queue2 的前端的元素即为新入栈的元素
function MyStack(){
this.queue1=[];
this.queue2=[];
}
MyStack.prototype.push=function(x){
this.queue2.push(x);
while(this.queue1.length){
this.queue2.push(this.queue1.shift());
}
[this.queue1,this.queue2]=[this.queue2,this.queue1];
}
MyStack.prototype.pop=function(){
return this.queue1.shift();
}
MyStack.prototype.top=function(){
return this.queue1[0];

}
MyStack.prototype.empty=function(){
return this.queue1.length==0
}

性能分析:

  • 时间复杂度: 入栈操作 O(n),其余操作都是 O(1)

入栈操作需要将 queue1中的 n 个元素出队,并入队 n+1 个元素到 queue2,共有 2n+1 次操作,每次出队和入队操作的时间复杂度都是 O(1),因此入栈操作的时间复杂度是 O(n)。

出栈操作对应将 queue1的前端元素出队,时间复杂度是 O(1)。

获得栈顶元素操作对应获得 queue1的前端元素,时间复杂度是 O(1)。

判断栈是否为空操作只需要判断 queue1是否为空,时间复杂度是 O(1)。

  • 空间复杂度:O(n),其中 n 是栈内的元素。需要使用两个队列存储栈内的元素。

方案B 一个队列

思路:

  • 入栈操作时,首先获得入栈前的元素个数 n
  • 将元素入队到队列
  • 将队列中的前 n 个元素(即除了新入栈的元素之外的全部元素)依次出队并入队到队列
  • 此时队列的前端的元素即为新入栈的元素,且队列的前端和后端分别对应栈顶和栈底。
function MyStack(){
this.queue=[];
}
MyStack.prototype.push=function(x){
let n=this.queue.length;
this.queue.push(x);
for(let i=0;i<n;i++){
this.queue.push(this.queue.shift());
}
}
MyStack.prototype.pop=function(){
return this.queue.shift();
}
MyStack.prototype.top=function(){
return this.queue[0];
}
MyStack.prototype.empty=function(){
return this.queue.length==0;
}

性能分析:

  • 时间复杂度:入栈操作 O(n),其余操作都是 O(1)。

入栈操作需要将队列中的 n 个元素出队,并入队 n+1 个元素到队列,共有 2n+1 次操作,每次出队和入队操作的时间复杂度都是 O(1),因此入栈操作的时间复杂度是 O(n)。

出栈操作对应将队列的前端元素出队,时间复杂度是 O(1)。

获得栈顶元素操作对应获得队列的前端元素,时间复杂度是 O(1)。

判断栈是否为空操作只需要判断队列是否为空,时间复杂度是 O(1)。

  • 空间复杂度:O(n),其中 n 是栈内的元素。需要使用一个队列存储栈内的元素。

广度优先遍历