Implement a Queue with O(1) Time Complexity for Retrieving the Minimum Value
Problem Description
Design a queue that, in addition to supporting basic queue operations (enqueue, dequeue, peek front), can retrieve the minimum value in the queue with O(1) time complexity. Assume the elements stored in the queue are comparable.
Solution Approach
The characteristic of a queue is First-In-First-Out (FIFO), and the minimum value changes dynamically as elements are dequeued. For example, if the minimum value is at the front of the queue, it needs to be updated after dequeuing. To achieve O(1) retrieval of the minimum value, we combine the properties of a standard queue and a monotonic queue:
- Main Queue: Use a standard queue (e.g., LinkedList) to store all elements, supporting normal enqueue and dequeue operations.
- Auxiliary Deque: Use a double-ended queue (e.g., ArrayDeque) to maintain a non-strictly monotonic increasing sequence, where the front always holds the current minimum value in the queue.
Step-by-Step Details
-
Initialization
- Create two queues:
queue(main queue) andminDeque(auxiliary double-ended queue).
- Create two queues:
-
Enqueue Operation (push)
- Add the new element
xtoqueue. - Process
minDeque: Starting from the tail, if the tail element is greater thanx, repeatedly pop from the tail (because these older elements will be dequeued afterxand cannot become the minimum) untilminDequeis empty or the tail element ≤x. - Add
xto the tail ofminDeque. - Time Complexity: Each element enters and leaves
minDequeat most once, amortized O(1).
- Add the new element
-
Dequeue Operation (pop)
- If
queueis empty, throw an error. - Pop the front element
valfromqueue. - If
valequals the front element ofminDeque, it means the current minimum value is being dequeued, so synchronously pop the front ofminDeque. - Time Complexity: O(1).
- If
-
Get Minimum Operation (getMin)
- Directly return the front element of
minDeque(if the queue is not empty). - Time Complexity: O(1).
- Directly return the front element of
-
Other Operations
peek(get front): Return the front ofqueue, O(1).isEmpty: Check ifqueueis empty, O(1).
Example Walkthrough
Operation sequence: push(3), push(1), push(2), getMin(), pop(), getMin()
- push(3):
queue→ [3]
minDeque→ [3] (added directly initially) - push(1):
queue→ [3, 1]
Tail ofminDequeis 3 > 1, pop 3 →minDequebecomes empty, then add 1 → [1] - push(2):
queue→ [3, 1, 2]
Tail ofminDequeis 1 ≤ 2, add 2 directly → [1, 2] - getMin() → 1 (front of
minDeque) - pop():
Pop front 3 fromqueue, but 3 ≠ front ofminDeque(which is 1), sominDequeremains unchanged → [1, 2] - getMin() → 1
Complexity Analysis
- Time Complexity: All operations are amortized O(1).
- Space Complexity: O(n), worst-case when all elements are simultaneously present in
minDeque(e.g., input is a strictly decreasing sequence).
Key Points
- The auxiliary queue
minDequemaintains a non-strictly increasing sequence, allowing duplicate elements (e.g., after push(1), push(1) again, both 1s need to be retained to correctly handle dequeues). - During dequeue, the front of
minDequeis popped only if it equals the front of the main queue, to avoid prematurely losing the minimum value.
Code Implementation (Java)
import java.util.*;
class MinQueue {
private Queue<Integer> queue;
private Deque<Integer> minDeque;
public MinQueue() {
queue = new LinkedList<>();
minDeque = new ArrayDeque<>();
}
public void push(int x) {
queue.offer(x);
while (!minDeque.isEmpty() && minDeque.peekLast() > x) {
minDeque.pollLast();
}
minDeque.offerLast(x);
}
public int pop() {
if (queue.isEmpty()) throw new NoSuchElementException();
int val = queue.poll();
if (val == minDeque.peekFirst()) {
minDeque.pollFirst();
}
return val;
}
public int getMin() {
if (minDeque.isEmpty()) throw new NoSuchElementException();
return minDeque.peekFirst();
}
public int peek() {
if (queue.isEmpty()) throw new NoSuchElementException();
return queue.peek();
}
public boolean isEmpty() {
return queue.isEmpty();
}
}