Skip to main content

Implement Queue using Stacks

LeetCode 232 | Difficulty: Easy​

Easy

Problem Description​

Implement a first in first out (FIFO) queue using only two stacks. The implemented queue should support all the functions of a normal queue (push, peek, pop, and empty).

Implement the MyQueue class:

- `void push(int x)` Pushes element x to the back of the queue.

- `int pop()` Removes the element from the front of the queue and returns it.

- `int peek()` Returns the element at the front of the queue.

- `boolean empty()` Returns `true` if the queue is empty, `false` otherwise.

Notes:

- You must use **only** standard operations of a stack, which means only `push to top`, `peek/pop from top`, `size`, and `is empty` operations are valid.

- Depending on your language, the stack may not be supported natively. You may simulate a stack using a list or deque (double-ended queue) as long as you use only a stack's standard operations.

Example 1:

Input
["MyQueue", "push", "push", "peek", "pop", "empty"]
[[], [1], [2], [], [], []]
Output
[null, null, null, 1, 1, false]

Explanation
MyQueue myQueue = new MyQueue();
myQueue.push(1); // queue is: [1]
myQueue.push(2); // queue is: [1, 2] (leftmost is front of the queue)
myQueue.peek(); // return 1
myQueue.pop(); // return 1, queue is [2]
myQueue.empty(); // return false

Constraints:

- `1 <= x <= 9`

- At most `100` calls will be made to `push`, `pop`, `peek`, and `empty`.

- All the calls to `pop` and `peek` are valid.

Follow-up: Can you implement the queue such that each operation is amortized O(1) time complexity? In other words, performing n operations will take overall O(n) time even if one of those operations may take longer.

Topics: Stack, Design, Queue


Approach​

Stack​

Use a stack (LIFO) to track elements that need future processing. Process elements when a "trigger" condition is met (e.g., finding a smaller/larger element). Monotonic stack maintains elements in sorted order for next greater/smaller element problems.

When to use

Matching brackets, next greater element, evaluating expressions, backtracking history.

Design​

Choose the right data structures to meet the required time complexities for each operation. Consider hash maps for O(1) access, doubly-linked lists for O(1) insertion/deletion, and combining structures for complex requirements.

When to use

Implementing a data structure or system with specific operation time requirements.


Solutions​

Solution 1: C# (Best: 96 ms)​

MetricValue
Runtime96 ms
Memory24 MB
Date2020-01-01
Solution
public class MyQueue {

int top = -1;
private Stack<int> s1 = new Stack<int>();
private Stack<int> s2 = new Stack<int>();
/** Initialize your data structure here. */
public MyQueue()
{

}

/** Push element x to the back of queue. */
public void Push(int x)
{
s1.Push(x);
if(s1.Count == 1) top = x;
}

/** Removes the element from in front of queue and returns that element. */
public int Pop()
{
while(s1.Count != 0)
{
s2.Push(s1.Pop());

}
var popped = s2.Pop();
top = s2.Count != 0 ? s2.Peek() : -1;
while(s2.Count != 0)
{
s1.Push(s2.Pop());
}
return popped;

}

/** Get the front element. */
public int Peek()
{
return s1.Count != 0 ? top : -1;
}

/** Returns whether the queue is empty. */
public bool Empty()
{
return s1.Count == 0;
}
}

/**
* Your MyQueue object will be instantiated and called as such:
* MyQueue obj = new MyQueue();
* obj.Push(x);
* int param_2 = obj.Pop();
* int param_3 = obj.Peek();
* bool param_4 = obj.Empty();
*/

Complexity Analysis​

ApproachTimeSpace
Stack$O(n)$$O(n)$

Interview Tips​

Key Points
  • Start by clarifying edge cases: empty input, single element, all duplicates.
  • Think about what triggers a pop: is it finding a match, or finding a smaller/larger element?
  • Clarify the expected time complexity for each operation before choosing data structures.