Design a SpecialQueue that supports enqueue(x), dequeue(), getFront(), and getMin() in O(1) time.
- enqueue(x) → insert element x at the rear of the queue
- dequeue() → remove the element from the front of the queue
- getFront() → return the front element without removing; -1 if empty
- getMin() → return the minimum element in the queue; -1 if empty
Input: operations[] = [enqueue(4), enqueue(2), enqueue(1), getMin(), enqueue(6), dequeue(), getFront(), getMin()]
Output: [1, 2, 1]
Explanation:
enqueue(4): Queue = [4]
enqueue(2): Queue = [4, 2]
enqueue(1): Queue = [4, 2, 1]
getMin(): Minimum element is 1
enqueue(6): Queue = [4, 2, 1, 6]
dequeue(): Removes 4 → Queue = [2, 1, 6]
getFront(): Front element is 2
getMin(): Minimum element is 1
[Approach] Using Dequeue - O(1) Time and O(n) Space
The idea is to use an auxiliary Deque to keep track of minimum elements while maintaining normal queue operations. By storing elements in sorted order inside the Deque, we can always retrieve the minimum in O(1) time.
Working of the Approach:
Enqueue(x):
-> Insert x into the main queue (q1).
-> In the deque (q2), remove all elements from the back that are larger than x (since they can’t be the minimum anymore), then insert x at the back.
Dequeue():
-> Remove the front element from the main queue (q1).
-> If this element is equal to the front of the deque (q2), also remove it from the deque to keep both structures in sync.
getMin(): Return the front of the deque (q2.front()), which always stores the current minimum element in the queue.
getFront(): Return the front element of the main queue (q1.front()), i.e., the element that would be removed first if dequeue() is called.
Note: In JavaScript, there’s no built-in queue, so we implement a custom queue using a doubly linked list for O(1) enqueue and dequeue.
C++
#include <iostream>
#include <queue>
#include <deque>
using namespace std;
class SpecialQueue {
queue<int> q1;
deque<int> q2;
public:
// Insert element into the queue
void enqueue(int x) {
q1.push(x);
while (!q2.empty() && q2.back() > x) {
q2.pop_back();
}
q2.push_back(x);
}
// Remove element from the queue
void dequeue() {
if (q1.empty()) {
cout << "Queue is empty\n";
return;
}
int frontVal = q1.front();
if (frontVal == q2.front()) {
q2.pop_front();
}
q1.pop();
}
// Get minimum element
int getMin() {
if (q1.empty()) {
cout << "Queue is empty\n";
return -1;
}
return q2.front();
}
// Get front element
int getFront() {
if (q1.empty()) {
cout << "Queue is empty\n";
return -1;
}
return q1.front();
}
};
int main() {
SpecialQueue q;
q.enqueue(4);
q.enqueue(2);
q.enqueue(1);
cout << q.getMin() <<" ";
q.enqueue(6);
q.dequeue();
cout << q.getFront() <<" ";
cout << q.getMin() <<" ";
return 0;
}
Java
import java.util.Queue;
import java.util.Deque;
import java.util.LinkedList;
class SpecialQueue {
Queue<Integer> q1 = new LinkedList<>();
Deque<Integer> q2 = new LinkedList<>();
// Insert element into the queue
public void enqueue(int x) {
q1.add(x);
while (!q2.isEmpty() && q2.peekLast() > x) {
q2.removeLast();
}
q2.addLast(x);
}
// Remove element from the queue
public void dequeue() {
if (q1.isEmpty()) {
System.out.println("Queue is empty");
return;
}
int frontVal = q1.peek();
if (frontVal == q2.peekFirst()) {
q2.removeFirst();
}
q1.remove();
}
// Get minimum element
public int getMin() {
if (q1.isEmpty()) {
System.out.println("Queue is empty");
return -1;
}
return q2.peekFirst();
}
// Get front element
public int getFront() {
if (q1.isEmpty()) {
System.out.println("Queue is empty");
return -1;
}
return q1.peek();
}
}
public class GFG {
public static void main(String[] args) {
SpecialQueue q = new SpecialQueue();
q.enqueue(4);
q.enqueue(2);
q.enqueue(1);
System.out.print(q.getMin() + " ");
q.enqueue(6);
q.dequeue();
System.out.print(q.getFront() + " ");
System.out.print(q.getMin() + " ");
}
}
Python
from collections import deque
class SpecialQueue:
def __init__(self):
# main queue
self.q1 = deque()
self.q2 = deque()
# Insert element into the queue
def enqueue(self, x):
self.q1.append(x)
while self.q2 and self.q2[-1] > x:
self.q2.pop()
self.q2.append(x)
# Remove element from the queue
def dequeue(self):
if not self.q1:
print("Queue is empty")
return
frontVal = self.q1.popleft()
if frontVal == self.q2[0]:
self.q2.popleft()
# Get minimum element
def getMin(self):
if not self.q1:
print("Queue is empty")
return -1
return self.q2[0]
# Get front element
def getFront(self):
if not self.q1:
print("Queue is empty")
return -1
return self.q1[0]
if __name__ == "__main__":
q = SpecialQueue()
q.enqueue(4)
q.enqueue(2)
q.enqueue(1)
print(q.getMin(), end=" ")
q.enqueue(6)
q.dequeue()
print(q.getFront(), end=" ")
print(q.getMin(), end=" ")
C#
using System;
using System.Collections.Generic;
class SpecialQueue {
Queue<int> q1 = new Queue<int>();
LinkedList<int> q2 = new LinkedList<int>();
// Insert element into the queue
public void enqueue(int x) {
q1.Enqueue(x);
while (q2.Count > 0 && q2.Last.Value > x) {
q2.RemoveLast();
}
q2.AddLast(x);
}
// Remove element from the queue
public void dequeue() {
if (q1.Count == 0) {
Console.WriteLine("Queue is empty");
return;
}
int frontVal = q1.Peek();
if (frontVal == q2.First.Value) {
q2.RemoveFirst();
}
q1.Dequeue();
}
// Get minimum element
public int getMin() {
if (q1.Count == 0) {
Console.WriteLine("Queue is empty");
return -1;
}
return q2.First.Value;
}
// Get front element
public int getFront() {
if (q1.Count == 0) {
Console.WriteLine("Queue is empty");
return -1;
}
return q1.Peek();
}
}
class GFG {
static void Main(string[] args) {
SpecialQueue q = new SpecialQueue();
q.enqueue(4);
q.enqueue(2);
q.enqueue(1);
Console.Write(q.getMin() + " ");
q.enqueue(6);
q.dequeue();
Console.Write(q.getFront() + " ");
Console.Write(q.getMin() + " ");
}
}
JavaScript
// Node class for doubly linked list
class Node {
constructor(data) {
this.data = data;
this.prev = null;
this.next = null;
}
}
// Queue implemented using doubly linked list
class Queue {
constructor() {
this.front = null;
this.rear = null;
this.size = 0;
}
// Add element at the rear
enqueue(x) {
let node = new Node(x);
if (!this.rear) {
this.front = this.rear = node;
} else {
node.prev = this.rear;
this.rear.next = node;
this.rear = node;
}
this.size++;
}
// Remove element from the front
dequeue() {
if (!this.front) return null;
let val = this.front.data;
this.front = this.front.next;
if (this.front) this.front.prev = null;
else this.rear = null;
this.size--;
return val;
}
// Get front element
getFront() {
return this.front ? this.front.data : null;
}
isEmpty() {
return this.size === 0;
}
}
// SpecialQueue using Queue class and maintaining min
class SpecialQueue {
constructor() {
this.q1 = new Queue(); // main queue
this.q2 = new Queue(); // track minimum
}
// Insert element into the queue
enqueue(x) {
this.q1.enqueue(x);
// maintain min queue
while (!this.q2.isEmpty() && this.q2.rear.data > x) {
// remove from rear
this.q2.rear = this.q2.rear.prev;
if (this.q2.rear) this.q2.rear.next = null;
else this.q2.front = null;
this.q2.size--;
}
this.q2.enqueue(x);
}
// Remove element from the queue
dequeue() {
if (this.q1.isEmpty()) {
console.log("Queue is empty");
return;
}
let frontVal = this.q1.dequeue();
if (frontVal === this.q2.getFront()) {
this.q2.dequeue();
}
}
// Get minimum element
getMin() {
if (this.q1.isEmpty()) {
console.log("Queue is empty");
return -1;
}
return this.q2.getFront();
}
// Get front element
getFront() {
if (this.q1.isEmpty()) {
console.log("Queue is empty");
return -1;
}
return this.q1.getFront();
}
}
// Driver Code
const q = new SpecialQueue();
q.enqueue(4);
q.enqueue(2);
q.enqueue(1);
process.stdout.write(q.getMin() + " ");
q.enqueue(6);
q.dequeue();
process.stdout.write(q.getFront() + " ");
process.stdout.write(q.getMin() + " ");
Explore
DSA Fundamentals
Data Structures
Algorithms
Advanced
Interview Preparation
Practice Problem