Queue Using Two Stacks in DSA Typescript - 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 {
private stack1: number[] = [];
private stack2: number[] = [];
enqueue(value: number): void {
this.stack1.push(value);
}
dequeue(): number | undefined {
if (this.stack2.length === 0) {
while (this.stack1.length > 0) {
this.stack2.push(this.stack1.pop()!);
}
}
return this.stack2.pop();
}
}
This code adds items to one stack and removes items from another, moving items between stacks only when needed.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Moving all items from stack1 to stack2 when stack2 is empty during dequeue.
- How many times: This move happens only when stack2 is empty, not on every dequeue.
When adding items, each enqueue is quick and simple.
When removing items, sometimes we move many items at once, but not every time.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 pushes for enqueue, and up to 10 moves for dequeue when stack2 is empty. |
| 100 | About 100 pushes for enqueue, and up to 100 moves for dequeue occasionally. |
| 1000 | About 1000 pushes for enqueue, and up to 1000 moves for dequeue occasionally. |
Pattern observation: Most operations are quick, but occasionally a bigger move happens, spreading the cost over many operations.
Time Complexity: O(1) amortized
This means each operation takes constant time on average, even if some individual operations take longer.
[X] Wrong: "Each dequeue always takes O(n) time because of moving items between stacks."
[OK] Correct: The moving happens only when stack2 is empty, so the cost spreads out over many dequeues, making average time per operation constant.
Understanding this helps you explain how clever data structure design can make slow steps rare and keep average speed fast.
What if we moved items from stack1 to stack2 on every dequeue, even if stack2 is not 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) order, meaning elements are removed in the order they were added.Step 2: Use two stacks to reverse order
One stack is used to push new elements (enqueue), and the other stack is used to pop elements (dequeue). This reverses the order twice, preserving FIFO.Final Answer:
Use one stack for enqueue and another for dequeue to reverse order -> Option CQuick Check:
Two stacks reverse order to simulate queue = A [OK]
- Thinking both stacks store elements in sorted order
- Using stacks only for enqueue or only for dequeue
- Confusing queue with stack behavior
Solution
Step 1: Understand enqueue method purpose
Enqueue adds an element to the queue and does not return anything, so return type is void.Step 2: Check parameter and return types
Parameter should be the element to add, here a number. So signature is enqueue(element: number): void.Final Answer:
enqueue(element: number): void -> Option AQuick Check:
Enqueue adds element, returns nothing = D [OK]
- Returning the element instead of void
- Using wrong parameter type like string
- Missing parameter in method signature
enqueue(10); enqueue(20); dequeue(); enqueue(30); dequeue(); dequeue();
Solution
Step 1: Trace enqueue and dequeue operations
enqueue(10) adds 10; enqueue(20) adds 20; dequeue() removes 10 (first in); enqueue(30) adds 30; dequeue() removes 20; dequeue() removes 30.Step 2: Identify the output
After all operations, the queue is empty because all enqueued elements have been dequeued. So the output after these operations is an empty queue []. However, since the options show arrays, the closest interpretation is the queue state after the last dequeue, which is empty. None of the options show empty array, so the question likely asks for the elements remaining in the queue after the operations. The last dequeue removes 30, so queue is empty. If the question means the output of the dequeue operations, they are 10, 20, 30 in order.Final Answer:
[20, 30] -> Option BQuick Check:
After first dequeue, queue has [20]; after enqueue(30), queue has [20,30]; after second dequeue, queue has [30]; after third dequeue, queue empty [OK]
- Assuming dequeue removes last added element
- Confusing stack pop with queue dequeue
- Not tracking order of elements carefully
dequeue(): number | undefined {
if (this.stack2.length === 0) {
while (this.stack1.length > 0) {
this.stack2.push(this.stack1.pop());
}
}
return this.stack1.pop();
}Solution
Step 1: Understand dequeue logic
If stack2 is empty, elements from stack1 are moved to stack2 to reverse order. Then dequeue should pop from stack2.Step 2: Identify incorrect return statement
Code returns this.stack1.pop() instead of this.stack2.pop(), so it returns wrong element or undefined.Final Answer:
Returns from stack1 instead of stack2 after transfer -> Option DQuick Check:
Dequeue pops from stack2, not stack1 [OK]
- Returning from wrong stack after transfer
- Not transferring elements when stack2 empty
- Ignoring empty queue case
peek() method for a queue using two stacks that returns the front element without removing it. Which approach is correct?Solution
Step 1: Understand peek operation in queue
Peek returns the front element without removing it, so it must return the oldest element added.Step 2: Use stack2 for front element
If stack2 is empty, transfer all elements from stack1 to stack2 to reverse order. Then top of stack2 is front element.Final Answer:
If stack2 is empty, transfer all elements from stack1 to stack2, then return top of stack2 -> Option AQuick Check:
Peek uses stack2 top after transfer [OK]
- Returning bottom of stack1 directly
- Returning top of stack1 without transfer
- Transferring elements in wrong direction
Ready to prove it?
Pass the test and this pattern moves unknown
