Implement Circular queue.

A circular queue is a data structure that uses a fixed-size array or a linked list to represent a queue. In a circular queue, when the end of the queue is reached, it wraps around to the beginning.

  • The circular queue has the advantage of efficiently utilizing space and avoiding the need to shift elements when the front of the queue is dequeued.
  • A circular queue maintains two pointers: front and rear.
  • front points to the front (head) of the queue, and rear points to the rear (tail) of the queue.
  • Initially, both pointers are set to -1 to indicate an empty queue.
  • The enqueue operation adds an element to the rear of the queue.
  • The dequeue operation removes an element from the front of the queue.

#include <iostream>
#include <vector>

class CircularQueue {
private:
    std::vector<int> array;
    int front;
    int rear;
    int size;
    int capacity;

public:
    CircularQueue(int capacity) : array(capacity), front(-1), rear(-1), size(0), capacity(capacity) {}

    bool isEmpty() const {
        return size == 0;
    }

    bool isFull() const {
        return size == capacity;
    }

    void enqueue(int value) {
        if (isFull()) {
            std::cout << "Queue is full. Cannot enqueue." << std::endl;
            return;
        }

        if (isEmpty()) {
            front = 0;
        }

        rear = (rear + 1) % capacity;
        array[rear] = value;
        size++;

        std::cout << "Enqueued: " << value << std::endl;
    }

    void dequeue() {
        if (isEmpty()) {
            std::cout << "Queue is empty. Cannot dequeue." << std::endl;
            return;
        }

        std::cout << "Dequeued: " << array[front] << std::endl;

        if (front == rear) {
            // Last element is dequeued, reset front and rear
            front = rear = -1;
        } else {
            front = (front + 1) % capacity;
        }

        size--;
    }

    int frontValue() const {
        if (isEmpty()) {
            std::cerr << "Queue is empty. No front value." << std::endl;
            return -1; // Assuming -1 represents an invalid value in this context
        }

        return array[front];
    }

    int rearValue() const {
        if (isEmpty()) {
            std::cerr << "Queue is empty. No rear value." << std::endl;
            return -1; // Assuming -1 represents an invalid value in this context
        }

        return array[rear];
    }
};

int main() {
    CircularQueue cq(5);

    cq.enqueue(1);
    cq.enqueue(2);
    cq.enqueue(3);

    std::cout << "Front: " << cq.frontValue() << ", Rear: " << cq.rearValue() << std::endl;

    cq.dequeue();
    cq.enqueue(4);
    cq.enqueue(5);
    cq.enqueue(6); // Trying to enqueue when the queue is full

    std::cout << "Front: " << cq.frontValue() << ", Rear: " << cq.rearValue() << std::endl;

    cq.dequeue();
    cq.dequeue();
    cq.dequeue();
    cq.dequeue(); // Trying to dequeue when the queue is empty

    return 0;
}

Output:

Enqueued: 1
Enqueued: 2
Enqueued: 3
Front: 1, Rear: 3
Dequeued: 1
Enqueued: 4
Enqueued: 5
Enqueued: 6
Front: 2, Rear: 6
Dequeued: 2
Dequeued: 3
Dequeued: 4
Dequeued: 5