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).
Notes
- Only standard operations of a queue are allowed: push to back, peek/pop from front, size, and isEmpty.
- Simulating a queue using a list or deque is allowed.
Example
Input:
Approach
We use two queues, queue1 and queue2:
- Push elements to
queue1.
- For
pop and top, transfer all elements except the last one from queue1 to queue2. The last element of queue1 represents the top of the stack.
- Swap the roles of
queue1 and queue2 after each operation.
Steps
-
Push:
- Add the element to
queue1.
-
Pop:
- Transfer all elements from
queue1 to queue2, except the last element.
- Remove and return the last element of
queue1.
- Swap
queue1 and queue2.
-
Top:
- Transfer all elements from
queue1 to queue2, except the last element.
- Retrieve and store the last element of
queue1.
- Push the last element back to
queue2.
- Swap
queue1 and queue2.
-
Empty:
- Check if
queue1 is empty.
Time & Space Complexity
- Time Complexity:
Push: O(1) (constant time to enqueue).
Pop: O(n) (transfer elements from queue1 to queue2).
Top: O(n) (same logic as pop).
Empty: O(1) (constant time to check if a queue is empty).
- Space Complexity: O(n), where
n is the total number of elements.
Code Snippet
-
MyStack():
- Initialize
queue1 = [], queue2 = [].
-
push(1):
- Add
1 to queue1: queue1 = [1].
-
push(2):
- Add
2 to queue1: queue1 = [1, 2].
-
top():
- Transfer all but the last element from
queue1 to queue2.
queue2 = [1], queue1 = [2].
- Return
2.
-
pop():
- Transfer all but the last element from
queue1 to queue2.
queue2 = [1], queue1 = [2].
- Remove and return
2.
- Swap queues:
queue1 = [1], queue2 = [].
-
empty():
- Check if
queue1 is empty: false.
Complexity Analysis
| Operation | Time Complexity | Space Complexity |
|---|
| Push | O(1) | O(n) |
| Pop | O(n) | O(n) |
| Top | O(n) | O(n) |
| Empty | O(1) | O(1) |
Conclusion
- Using two queues allows us to implement a stack with all its standard operations while adhering to the constraints of using only queue operations.
- The approach highlights how different data structures can be combined to achieve desired functionality.
- This implementation ensures a balance between functionality and efficiency.