A queue is a fundamental data structure that operates on the principle of “First In, First Out” (FIFO), where the first element added to the queue is the first one to be removed. This structure resembles a real-world queue like people waiting in line; the person who joins first is the first one to be served.
In C++, the Standard Template Library (STL) provides an efficient implementation of queues using the std::queue container adapter. This introduction covers the fundamental concepts of queues, their types, and how to implement them in C++.
Queues typically support the following operations:
std::queueThe C++ STL provides a straightforward way to implement simple queues using the std::queue class template, which requires a container, typically std::deque or std::list.
#include <iostream>
#include <queue>
int main() {
    std::queue<int> q;
    // Enqueue elements
    q.push(1);
    q.push(2);
    q.push(3);
    // Check the front element
    std::cout << "Front element: " << q.front() << std::endl;
    // Dequeue elements
    q.pop();
    std::cout << "After pop, front element: " << q.front() << std::endl;
    return 0;
}std::dequeThe STL also provides the std::deque container, which supports fast insertions and deletions at both ends. It can be used directly to implement a double-ended queue.
#include <deque>
#include <iostream>
int main() {
    std::deque<int> dq;
    // Add to both ends
    dq.push_back(1);
    dq.push_back(2);
    dq.push_front(0);
    // Check both ends
    std::cout << "Front element: " << dq.front() << std::endl;
    std::cout << "Back element: " << dq.back() << std::endl;
    // Remove from both ends
    dq.pop_front();
    dq.pop_back();
    std::cout << "After popping, front element: " << dq.front() << std::endl;
    return 0;
}std::priority_queuePriority queues organize elements based on a specified comparison function. The default is a max-heap, but a min-heap can also be used.
#include <iostream>
#include <queue>
#include <vector>
int main() {
    // Priority queue with default comparator (max-heap)
    std::priority_queue<int> max_heap;
    max_heap.push(10);
    max_heap.push(20);
    max_heap.push(15);
    // Access top element
    std::cout << "Max-heap top element: " << max_heap.top() << std::endl;
    // Remove top element
    max_heap.pop();
    std::cout << "After pop, top element: " << max_heap.top() << std::endl;
    // Min-heap using custom comparator
    auto comp = [](int a, int b) { return a > b; };
    std::priority_queue<int, std::vector<int>, decltype(comp)> min_heap(comp);
    min_heap.push(10);
    min_heap.push(20);
    min_heap.push(15);
    std::cout << "Min-heap top element: " << min_heap.top() << std::endl;
    return 0;
}Custom queues can be implemented using linked lists or arrays to achieve specific behavior.
An array-based implementation allows random access to elements but requires careful management of indexes.
#include <iostream>
#define MAX 1000
class ArrayQueue {
    int arr[MAX];
    int front, rear;
public:
    ArrayQueue() : front(-1), rear(-1) {}
    void enqueue(int x) {
        if (rear == MAX - 1) {
            std::cout << "Queue Overflow\n";
            return;
        }
        if (front == -1)
            front = 0;
        arr[++rear] = x;
    }
    void dequeue() {
        if (front == -1 || front > rear) {
            std::cout << "Queue Underflow\n";
            return;
        }
        front++;
    }
    int peek() {
        if (front == -1 || front > rear) {
            std::cout << "Queue is Empty\n";
            return -1;
        }
        return arr[front];
    }
};
int main() {
    ArrayQueue q;
    q.enqueue(10);
    q.enqueue(20);
    std::cout << "Front element: " << q.peek() << std::endl;
    q.dequeue();
    std::cout << "After dequeue, front element: " << q.peek() << std::endl;
    return 0;
}A linked list implementation avoids overflow and provides dynamic sizing.
#include <iostream>
class Node {
public:
    int data;
    Node* next;
    Node(int val) : data(val), next(nullptr) {}
};
class LinkedListQueue {
    Node *front, *rear;
public:
    LinkedListQueue() : front(nullptr), rear(nullptr) {}
    void enqueue(int x) {
        Node* newNode = new Node(x);
        if (rear == nullptr) {
            front = rear = newNode;
            return;
        }
        rear->next = newNode;
        rear = newNode;
    }
    void dequeue() {
        if (front == nullptr) {
            std::cout << "Queue Underflow\n";
            return;
        }
        Node* temp = front;
        front = front->next;
        if (front == nullptr)
            rear = nullptr;
        delete temp;
    }
    int peek() {
        if (front == nullptr) {
            std::cout << "Queue is Empty\n";
            return -1;
        }
        return front->data;
    }
};
int main() {
    LinkedListQueue q;
    q.enqueue(10);
    q.enqueue(20);
    std::cout << "Front element: " << q.peek() << std::endl;
    q.dequeue();
    std::cout << "After dequeue, front element: " << q.peek() << std::endl;
    return 0;
}