Queue Using Two Stacks in DSA C++ - 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 the queue gets bigger?
Analyze the time complexity of the following code snippet.
class QueueUsingStacks {
stack<int> s1, s2;
public:
void enqueue(int x) {
s1.push(x);
}
int dequeue() {
if (s2.empty()) {
while (!s1.empty()) {
s2.push(s1.top());
s1.pop();
}
}
int val = s2.top();
s2.pop();
return val;
}
};
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 s1 to s2 when s2 is empty during dequeue.
- How many times: Each item moves at most twice: once pushed to s1, once moved to s2.
Adding items (enqueue) always takes the same small time, no matter how many items.
Removing items (dequeue) sometimes takes longer when moving items, but this happens rarely.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 pushes for enqueue, about 10 pops and moves for dequeue |
| 100 | About 100 pushes for enqueue, about 100 pops and moves for dequeue |
| 1000 | About 1000 pushes for enqueue, about 1000 pops and moves for dequeue |
Pattern observation: Each item is moved only a few times, so average time per operation stays steady.
Time Complexity: O(1) amortized
This means each add or remove operation takes about the same small time on average, even if some removals take longer.
[X] Wrong: "Each dequeue always takes time proportional to the number of items because of moving stacks."
[OK] Correct: Items are moved only once per enqueue, so the costly moves are spread out, making average time per dequeue small.
Understanding this helps you explain how clever data structure design can keep operations fast on average, a key skill in coding interviews.
"What if we moved items from s1 to s2 on every dequeue, not only when s2 is empty? How would the time complexity change?"
Practice
Queue Using Two Stacks?Solution
Step 1: Understand queue behavior
A queue follows FIFO (First In First Out), so the first element added should be the first removed.Step 2: Use two stacks to reverse order
One stack stores elements as they come (enqueue). The second stack reverses the order to allow dequeue in FIFO order.Final Answer:
Use one stack for enqueue and another for dequeue to reverse order -> Option BQuick Check:
Two stacks reverse order to simulate queue FIFO [OK]
- Thinking both stacks store elements in the same order
- Assuming stacks keep elements sorted
- Using one stack only for all operations
x into a stack named stack1 in C++?Solution
Step 1: Recall C++ stack push syntax
The standard C++ stack uses the methodpush()to add elements.Step 2: Match correct method name
Onlypushis the correct method; others likeadd,insert, orenqueueare invalid for stack.Final Answer:
stack1.push(x); -> Option AQuick Check:
Stack push method = push() [OK]
- Using enqueue() which is for queues
- Using add() or insert() which are not stack methods
- Confusing stack with vector methods
std::stack<int> s1, s2;
// Enqueue 1, 2, 3
s1.push(1);
s1.push(2);
s1.push(3);
// Dequeue operation
while (!s1.empty()) {
s2.push(s1.top());
s1.pop();
}
int front = s2.top();
s2.pop();
// What is the value of front?Solution
Step 1: Trace enqueue operations
Elements pushed into s1 in order: 1 (bottom), 2, 3 (top).Step 2: Transfer elements to s2 to reverse order
Moving all from s1 to s2 reverses order: s2 top is 1, bottom is 3.Step 3: Identify front element
Front of queue is s2.top() which is 1, then popped.Final Answer:
1 -> Option CQuick Check:
Queue front after transfer = 1 [OK]
- Assuming top of s1 is front (3 instead of 1)
- Not reversing stack order before dequeue
- Confusing stack top with queue front
void dequeue() {
if (s2.empty()) {
while (!s1.empty()) {
s2.push(s1.top());
s1.pop();
}
}
s1.pop();
}Solution
Step 1: Understand dequeue logic
Dequeue removes front element, which is on top of s2 after transfer.Step 2: Identify incorrect pop
Code pops from s1 after transfer, but front is on s2, so s2.pop() is correct.Final Answer:
Should pop from s2, not s1, after transfer -> Option DQuick Check:
Dequeue pops from s2 top [OK]
- Popping from wrong stack after transfer
- Not checking if both stacks are empty before dequeue
- Confusing enqueue and dequeue stacks
enqueue(x): Push element x to the queue.dequeue(): Remove and return the front element.Which approach ensures amortized O(1) time per operation?
Solution
Step 1: Understand amortized O(1) goal
We want to minimize costly transfers by doing them only when necessary.Step 2: Analyze each approach
Push all elements to s1 on enqueue; transfer to s2 only when s2 is empty during dequeue transfers elements from s1 to s2 only when s2 is empty during dequeue, spreading cost over many operations.Step 3: Reject other options
Options B, C, and D cause frequent or unnecessary transfers increasing time per operation.Final Answer:
Push all elements to s1 on enqueue; transfer to s2 only when s2 is empty during dequeue -> Option AQuick Check:
Transfer only when s2 empty = amortized O(1) [OK]
- Transferring elements on every enqueue or dequeue
- Pushing alternately causing imbalance
- Ignoring amortized cost analysis
Ready to prove it?
Pass the test and this pattern moves unknown
