Queue Using Two Stacks in DSA Python - Time & Space Complexity
We want to understand how fast operations run when we build a queue using two stacks.
Specifically, how does the time to add or remove items grow as we add more items?
Analyze the time complexity of the following code snippet.
class QueueUsingStacks:
def __init__(self):
self.stack_in = []
self.stack_out = []
def enqueue(self, item):
self.stack_in.append(item)
def dequeue(self):
if not self.stack_out:
while self.stack_in:
self.stack_out.append(self.stack_in.pop())
return self.stack_out.pop() if self.stack_out else None
This code adds items to one stack and removes items from another, moving items only when needed.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Moving all items from
stack_intostack_outwhenstack_outis empty. - How many times: This move happens only when
stack_outis empty, not on every dequeue.
Adding items is always quick, but removing items sometimes requires moving many items.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 moves when stack_out empties |
| 100 | About 100 moves occasionally, but mostly 1 per dequeue |
| 1000 | About 1000 moves occasionally, but mostly 1 per dequeue |
Pattern observation: Most dequeues are fast, but occasionally a slow step moves many items all at once.
Time Complexity: O(1) amortized per operation
This means on average, each add or remove takes a constant amount of time, even if some removals take longer.
[X] Wrong: "Each dequeue always takes time proportional to the number of items."
[OK] Correct: Because items are moved only once from stack_in to stack_out, so the cost spreads out over many dequeues.
Understanding this helps you explain how clever data structures can make slow steps rare, keeping average speed fast.
What if we moved items from stack_in to stack_out on every dequeue, even if stack_out is not empty? How would the time complexity change?
Practice
Queue using two stacks?Solution
Step 1: Understand queue order
A queue follows FIFO (First In First Out), meaning the first element added is the first removed.Step 2: Use two stacks to reverse order twice
One stack reverses the order when pushing elements, and the second stack reverses it back when popping, preserving FIFO.Final Answer:
Use two stacks to reverse the order twice to maintain FIFO -> Option AQuick Check:
Two stacks reverse order twice = FIFO queue [OK]
- Thinking one stack alone can maintain FIFO
- Confusing stack sorting with queue behavior
- Using a queue inside the stack implementation
x onto a stack named stack1 in Python?Solution
Step 1: Recall Python list methods for stack
Python lists useappend()to add elements at the end, simulating stack push.Step 2: Check other options
push()andadd()are not list methods;insert()requires an index.Final Answer:
stack1.append(x) -> Option BQuick Check:
Python stack push = append() [OK]
- Using push() which is not a Python list method
- Using insert() without index argument
- Confusing add() with set method
stack1 = [1, 2, 3]
stack2 = []
if not stack2:
while stack1:
stack2.append(stack1.pop())
print(stack2.pop())Solution
Step 1: Transfer elements from stack1 to stack2
Elements pop from stack1 in order 3, 2, 1 and append to stack2, so stack2 becomes [3, 2, 1].Step 2: Pop from stack2
Popping from stack2 removes last element, which is 1, the first element enqueued.Final Answer:
1 -> Option AQuick Check:
Two stacks reverse order, first out is 1 [OK]
- Assuming pop from stack1 directly returns front
- Confusing order after transfer
- Expecting 3 instead of 1
def dequeue():
if stack2.is_empty():
while stack1:
stack2.append(stack1.pop())
return stack1.pop()Solution
Step 1: Check stack transfer logic
Elements are moved from stack1 to stack2 when stack2 is empty to reverse order.Step 2: Identify pop operation error
After transfer, popping should be from stack2 to get the correct queue front, but code pops from stack1 incorrectly.Final Answer:
Popping from stack1 instead of stack2 after transfer -> Option CQuick Check:
Pop from stack2 after transfer, not stack1 [OK]
- Popping from wrong stack after transfer
- Using non-existent is_empty() method on list
- Ignoring empty queue check
Solution
Step 1: Understand enqueue operation
All new elements are pushed onto stack1, which is fast (O(1)).Step 2: Understand dequeue operation
When stack2 is empty, transfer all elements from stack1 to stack2 (reversing order), then pop from stack2. This transfer happens only when needed, spreading cost.Step 3: Analyze amortized time
Each element moves at most twice (push to stack1, move to stack2), so average cost per operation is O(1).Final Answer:
Push all elements to stack1 on enqueue; on dequeue, transfer all to stack2 if empty, then pop from stack2 -> Option DQuick Check:
Amortized O(1) by lazy transfer from stack1 to stack2 [OK]
- Transferring elements on every dequeue causing O(n) each time
- Popping directly from stack1 breaking queue order
- Alternating pushes causing confusion
Ready to prove it?
Pass the test and this pattern moves unknown
