24 Jan 2023, 11:33 PM
24 Jan 2023, 11:33 PM

Queues

A queue implements FIFO (first-in first-out) ordering. As in a line or queue at a ticket stand, items are removed from the data structure in the same order that they are added.

Queue implementation

In the below implementation, we have a Queue class that contains the Node class.

My Queue class implementation in C++

Here you can find the repository on GitHub with the complete code.

Queue.h
#pragma once

#include <iostream> 

/* Forward declaration of the template, so that operator<< is regognized as a template */
template<class T>
class Queue;

template<class T>
std::ostream& operator<< ostream& out, const Queue<T>& queue;

template<class T>    
class Queue {
    class Node {
        public:
            T data;
            Node* next;
            
            // default constructor
            Node() = default;
            // constructor with data
            Node(const T& value) : data(value), next(NULL) {}
    };
    protected:
        Node* first;
        Node* last;
    public:
        Queue() : first(NULL), last(NULL) {}
        Queue(const Queue& copyQueue);
        ~Queue();
        void initRandom(int size);
        T remove();
        void add(T value);
        T peek() const;
        bool isEmpty() const;
        void print() const;
        void clear();
        Queue<T>& operator= (const Queue<T>& queue);
        friend std::ostream& operator<< <> ostream& out, const Queue<T>& queue;
};
Queue.cpp
#include <iostream>

#include "Queue.h"

template<class T>
Queue<T>::Queue(const Queue& copyQueue) {
    Node* newFirst = this->first = new Node();
    
    for(auto tmp = copyQueue.first; tmp != NULL; tmp = tmp->next) {
        newFirst->next = new Node(tmp->data);
        newFirst = newFirst->next;
    }

    Node* tmp = this->first;
    this->first = this->first->next;
    delete tmp;
}

template<class T>
Queue<T>::~Queue() {
    this->clear();
}

template<class T>
void Queue<T>::initRandom(int size) {
    for(int i = 0; i < size; i++) {
            int random = rand() % 10;
            add(random);
    }
}

template<class T>
T Queue<T>::remove() {
    if(isEmpty())
        throw std::invalid_argument("The queue is empty!");
    Node *temp = this->first;
    this->first = this->first->next;
    int data = temp->data;
    delete temp;
    return data;
}

template<class T>
void Queue<T>::add(T value) {
    Node *node = new Node(value);
    if(isEmpty())
        this->first = this->last = node;
    else {
        this->last->next = node;
        this->last = this->last->next;
    }
}

template<class T>
T Queue<T>::peek() const {
    if(!isEmpty())
        return this->first->data;
}

template<class T>
bool Queue<T>::isEmpty() const {
    return this->first == NULL; 
}

template<class T>
void Queue<T>::clear() {
    while (!isEmpty())
        remove();
}

template<class T>
void Queue<T>::print() const {
    std::cout << *this; 
}
 
template<typename T>
Queue<T>& Queue<T>::operator= (const Queue<T>& copyQueue) {
    Queue<T> temp(copyQueue);
    std::swap(temp.top, this->top);
    return *this;
    /* Since I swapped the heads, now temp destructor will be called, but temp now is the 
        original queue */
}

template<class T>
std::ostream& operator<< ostream& out, const Queue<T>& queue {
    for(auto current = queue.first; current != NULL; current = current->next)
        out << current->data << " -> ";
    out << "NULL" << std::endl;
    return out;
}