Implement a last-in-first-out (LIFO) stack using only two queues. The implemented stack should support all the functions of a normal stack (push
, top
, pop
, and empty
).
Specifically, implement the MyStack
class with the following methods:
void push(int x)
: Pushes element x
to the top of the stack.int pop()
: Removes the element on the top of the stack and returns it.int top()
: Returns the element on the top of the stack.boolean empty()
: Returns true
if the stack is empty, false
otherwise.Important Constraints:
push to back
, peek/pop from front
, size
and is empty
.Example:
MyStack myStack = new MyStack();
myStack.push(1);
myStack.push(2);
myStack.top(); // return 2
myStack.pop(); // return 2
myStack.empty(); // return False
Provide an efficient implementation and analyze its time and space complexity. Additionally, consider edge cases and a follow-up question on implementing the stack using only one queue.
The task is to implement a Last-In-First-Out (LIFO) stack data structure using only two queues. The implemented stack should support the standard stack operations: push
, pop
, top
, and empty
. We are restricted to using only standard queue operations: push to back
, peek/pop from front
, size
, and is empty
.
A naive approach is not distinctly different from the optimal one in this case, given the constraints.
The key idea is to use two queues, let's call them q1
and q2
. When pushing an element onto the stack, we always add it to q1
. When we need to perform a pop or top operation, we transfer all elements from q1
to q2
except for the last element, which will be the top of the stack. Then we return (or pop) that last element. After this operation, we swap the names of q1
and q2
so that q1
is always the queue to which we add new elements.
Detailed Steps:
x
to q1
.q1
to q2
except the last element.q1
(which is the top of the stack).q1
and q2
.q1
to q2
except the last element.q1
(which is the top of the stack).q1
to q2
.q1
and q2
.q1
is empty.Example:
push(1)
push(2)
top() // returns 2
pop() // returns 2
empty() // returns false
class MyStack:
def __init__(self):
self.q1 = []
self.q2 = []
def push(self, x: int) -> None:
self.q1.append(x)
def pop(self) -> int:
while len(self.q1) > 1:
self.q2.append(self.q1.pop(0))
top = self.q1.pop(0)
self.q1, self.q2 = self.q2, self.q1
return top
def top(self) -> int:
while len(self.q1) > 1:
self.q2.append(self.q1.pop(0))
top = self.q1[0]
self.q2.append(self.q1.pop(0))
self.q1, self.q2 = self.q2, self.q1
return top
def empty(self) -> bool:
return not self.q1
O(n), where n is the maximum number of elements in the stack, because we are using two queues to store the elements.
pop()
or top()
are called on an empty stack, the problem statement says that the calls are valid which means the empty stack scenario does not need to be handled.We can implement the stack using a single queue. For the push
operation, we enqueue the element and then rotate the queue so that the newly added element becomes the front of the queue. This ensures LIFO behavior.
Detailed Steps:
x
to the queue.size
- 1 times by dequeuing from the front and enqueuing to the back.Code Implementation (Python):
class MyStack:
def __init__(self):
self.q = []
def push(self, x: int) -> None:
self.q.append(x)
for _ in range(len(self.q) - 1):
self.q.append(self.q.pop(0))
def pop(self) -> int:
return self.q.pop(0)
def top(self) -> int:
return self.q[0]
def empty(self) -> bool:
return not self.q
O(n), where n is the maximum number of elements in the stack, as we are using a single queue to store the elements.