This article introduces the implementation of queues through linked lists and pointers.
catalogue
1, Logical structure:
We use the circular linked list structure with tail pointer as shown in the following figure:
This structure is used because we need to operate the head and tail. If it is a leading pointer, the tail is not easy to find. If it is a circular linked list with tail pointer, the next of the tail is the head, which is very convenient for the operation at both ends.
2, Overview:
The following are the functions to be realized:
class Queue { private: class Node//A node that stores data, consisting of data and a pointer to the next node { public: int data; Node* next; //Constructor Node(int newdata = 0, Node* nextptr = NULL) :data(newdata), next(nextptr) {}; }; Node* last; public: Queue() :last(NULL) {};//Default constructor Queue(const Queue& origin);//copy constructor ~Queue();//Destructor Queue& operator=(const Queue& origin);//=Heavy load bool empty() const;//Air judgment void enqueue(int newdata);//Join the team bool dequeue();//Out of the team int front() const;//Team head data void display() const;//Traversal queue };
3, Related operations:
1. Copy constructor:
Queue::Queue(const Queue& origin) { last = NULL; if (!origin.empty()) { last = new Node(origin.last->data);//Make room for last first Node* orgptr = origin.last->next;//Pointer traversing origin Node* curptr = last;//Pointer to traverse the current queue //Traversal replication while (orgptr != origin.last)//The traversal stops at the last one { curptr->next = new Node(orgptr->data);//Open a new node at a time orgptr = orgptr->next; curptr = curptr->next; } curptr->next = last; } }
The comments have been given in the code. One thing to note is that our loop to origin Last stops. At this time, the next of the penultimate node in the current linked list is still null, so after the while loop, point the next of the penultimate node to last.
2. Destructor:
Queue::~Queue() { if (!empty()) { Node* ptr = last->next;//Pointer for traversal Node* nextptr = NULL;//Store next while (ptr != last) { nextptr = ptr->next;//Write down the next of the node to be deleted, otherwise it will not be found after deletion delete ptr; ptr = nextptr; } delete last; last = NULL; } }
The traversal pointer ptr is initialized to last - > next to avoid conflict with the conditions of the while loop. If ptr=last directly, the loop cannot start. Therefore, at the end, we have to delete the last that is not put into the loop.
3. = heavy load:
Queue& Queue::operator=(const Queue& origin) { if (this != &origin) { this->~Queue(); if (!origin.empty()) { last = new Node(origin.last->data); Node* orgptr = origin.last->next; Node* curptr = last; while (orgptr != origin.last) { curptr->next = new Node(orgptr->data); orgptr = orgptr->next; curptr = curptr->next; } curptr->next = last; } } return *this; }
This is basically the same as the copy constructor. The only difference is whether the destructor is called at the beginning. If you don't judge whether the destructor is on the right, it has been deleted when calling the destructor on line 5 and can't be copied, so you need to judge. If it is itself, no operation is required.
4. Air judgment:
The condition for judging null is that the tail is null.
bool Queue::empty() const { return (last == NULL); }
5. Header node data:
int Queue::front() const { if (empty()) { cerr << "empty"; exit(1); } else return last->next->data; }
6. Join the team:
Add new data at the end of the team.
void Queue::enqueue(int newdata) { if (empty()) { last = new Node(newdata); last->next = last; } else { last->next = new Node(newdata, last->next); last = last->next; } }
There are two cases where the unit column is empty and not empty,
1. When it is empty, it will open up space for last to store data. At this time, there is only one data, which is both the head and tail.
2. If it is not empty, put the new node in the next of last, and the next of this new node is the original last - > next, that is, the header pointer.
Finally, make the new node a new last.
7. Departure:
Delete the header node.
bool Queue::dequeue() { if (empty()) return false; else if (last->next == last) { delete last; last = NULL; return true; } else { Node* newfirst = last->next->next; delete last->next; last->next = newfirst; return true; } }
Three cases:
1. If the queue is empty, false will be returned directly.
2. When there is only one data in the queue, delete last directly.
3. When there are multiple elements in the queue, the pointer needs to be adjusted. The new head node is the original second node. Therefore, the next of last should point to the second node after deleting the head node.
8. Print queue data:
void Queue::display() const { if (!empty()) { Node* ptr = last->next; while (ptr != last) { cout << ptr->data << " "; ptr = ptr->next; } cout << ptr->data << endl; } }