Implement Queue using Stacks (opens in a new tab) Easy
Problem
Implement a first-in, first-out (FIFO) queue using only two stacks. The implemented queue should support all the functions of a normal queue (push, peek, pop, and empty).
Example
- push(1)
- push(2)
- peek() // Returns 1
- pop() // Returns 1
- empty() // Returns false
Summary
To solve this problem, we can use two stacks, inputStack
and outputStack
. The inputStack
will be used to store new elements when they are pushed to the queue. The outputStack
will be used to hold the elements in the correct order for popping or peeking. When popping or peeking, if the outputStack
is empty, we'll transfer all elements from the inputStack
to the outputStack
, thus reversing the order of the elements.
Solution
In TypeScript
class MyQueue {
private inputStack: number[];
private outputStack: number[];
constructor() {
this.inputStack = [];
this.outputStack = [];
}
push(x: number): void {
this.inputStack.push(x);
}
pop(): number | undefined {
this.moveElementsToOutputStackIfNeeded();
return this.outputStack.pop();
}
peek(): number | undefined {
this.moveElementsToOutputStackIfNeeded();
return this.outputStack[this.outputStack.length - 1];
}
empty(): boolean {
return this.inputStack.length === 0 && this.outputStack.length === 0;
}
private moveElementsToOutputStackIfNeeded(): void {
if (this.outputStack.length === 0) {
while (this.inputStack.length > 0) {
this.outputStack.push(this.inputStack.pop() as number);
}
}
}
}
In Python
class MyQueue:
def __init__(self):
"""
Initialize your data structure here.
"""
self.inputStack = []
self.outputStack = []
def push(self, x: int) -> None:
"""
Push element x to the back of queue.
"""
self.inputStack.append(x)
def pop(self) -> int:
"""
Removes the element from in front of queue and returns that element.
"""
self.moveElementsToOutputStackIfNeeded()
return self.outputStack.pop()
def peek(self) -> int:
"""
Get the front element.
"""
self.moveElementsToOutputStackIfNeeded()
return self.outputStack[-1]
def empty(self) -> bool:
"""
Returns whether the queue is empty.
"""
return len(self.inputStack) == 0 and len(self.outputStack) == 0
def moveElementsToOutputStackIfNeeded(self) -> None:
if len(self.outputStack) == 0:
while len(self.inputStack) > 0:
self.outputStack.append(self.inputStack.pop())
Step-by-step explanation
- Define a
MyQueue
class with two properties,inputStack
andoutputStack
. Both are initialized as empty arrays to represent the two stacks.
class MyQueue {
private inputStack: number[];
private outputStack: number[];
constructor() {
this.inputStack = [];
this.outputStack = [];
}
}
- Implement the
push
method. When an element is pushed to the queue, simply push it onto theinputStack
. The time complexity of this operation is O(1).
push(x: number): void {
this.inputStack.push(x);
}
- Implement the
pop
method. First, call themoveElementsToOutputStackIfNeeded
method to ensure that theoutputStack
has elements in the correct order for popping. Then, pop the top element from theoutputStack
. The amortized time complexity of this operation is O(1).
pop(): number | undefined {
this.moveElementsToOutputStackIfNeeded();
return this.outputStack.pop();
}
- Implement the
peek
method. Call themoveElementsToOutputStackIfNeeded
method to ensure that theoutputStack
has elements in the correct order for peeking. Then, return the top element of theoutputStack
without removing it. The amortized time complexity of this operation is O(1).
peek(): number | undefined {
this.moveElementsToOutputStackIfNeeded();
return this.outputStack[this.outputStack.length - 1];
}
- Implement the
empty
method. The queue is empty if bothinputStack
andoutputStack
are empty. The time complexity of this operation is O(1).
empty(): boolean {
return this.inputStack.length === 0 && this.outputStack.length === 0;
}
- Implement the
moveElementsToOutputStackIfNeeded
method, which is a helper function to transfer elements from theinputStack
to theoutputStack
when theoutputStack
is empty. This reverses the order of the elements, making it possible to achieve the desired queue behavior with two stacks.
private moveElementsToOutputStackIfNeeded(): void {
if (this.outputStack.length === 0) {
while (this.inputStack.length > 0) {
this.outputStack.push(this.inputStack.pop() as number);
}
}
}
Complexity Analysis
Time Complexity
The time complexity of the push operation is O(1), as we are only pushing elements onto the inputStack
.
The time complexity of the pop and peek operations is amortized O(1), as the elements are only transferred from the inputStack to the outputStack when the outputStack is empty. Since each element is transferred at most once, the average time complexity for pop and peek operations is constant.
The time complexity of the empty operation is O(1), as we are only checking the length of the inputStack and outputStack.
Space Complexity
The space complexity of this solution is O(n), as we are using two stacks to store the elements in the queue. In the worst case, all elements are stored in one of the stacks, and the space complexity will be proportional to the number of elements in the queue.