Article catalog
2.1 initialization and destruction
1, What is the queue?
Queue is also a linear table with the characteristic of "first in first out". It's like queuing in our life. People who queue early will turn early.
Queues can only be ejected at the head of the queue and inserted at the end of the queue.
2, Code implementation
1. Storage structure
The queue can be stored using array or linked list. It is inconvenient to use array to forward the whole queue when the queue head pops up. Therefore, this paper selects the linked list structure to realize this queue.
Node settings are the same as linked lists. A data field stores data and a pointer field stores the address of the next node. In addition, for the convenience of tail insertion, a structure is defined, including two pointer member variables at the beginning and end.
typedef int QDataType; typedef struct QueueNode { struct QueueNode* next; QDataType data; }QNode; typedef struct Queue { QNode* head; QNode* tail; }Queue;
2. Common interface functions
2.1 initialization and destruction
Initialization and destruction are similar to linked lists and relatively simple. When destroying, you need to set a next pointer to record the next node, so as not to find the next node after releasing the current node.
void QueueInit(Queue* q) { assert(q); q->head = q->tail = NULL; } void QueueDestroy(Queue* q) { assert(q); QNode* cur = q->head; while (cur) { QNode* next = cur->next; free(cur); cur = next; } q->head = q->tail = NULL; }
2.2 insertion and ejection
Entering the queue is like inserting the tail of a linked list, but due to the addition of a tail pointer, you can save the tail finding operation and insert it directly at the tail. At the same time, because there is no header node, when the linked list is empty, it should be treated as a special case.
Out of the queue, just release the head pointer and move it back one node. But there is a hole in it. But when there is only one node left in the queue, continue pop. The position of the head pointer is the same as that of the tail pointer. After the head pointer is free, the next field moved to the node is empty. However, at this time, the tail pointer still points to the node position just released, resulting in the problem of wild pointer. Therefore, we can set the tail pointer to null when the head pointer is null.
void QueuePush(Queue* q, QDataType data) { assert(q); QNode* newNode = (QNode*)malloc(sizeof(QNode)); if (!newNode) { printf("malloc fail"); exit(-1); } newNode->data = data; newNode->next = NULL; if (q->head == NULL) { q->head = q->tail = newNode; } else { q->tail->next = newNode; q->tail = newNode; } } // Queue leader out of queue void QueuePop(Queue* q) { assert(q); assert(!QueueEmpty(q)); QNode* next = q->head->next; free(q->head); q->head = next; if (q->head == NULL) { q->tail == NULL; } }
2.3 other interfaces
Other interfaces are relatively simple. Most of them can be implemented in one or two sentences of code.
// Get queue header element QDataType QueueFront(Queue* q) { assert(q); assert(!QueueEmpty(q)); return q->head->data; } // Get queue tail element QDataType QueueBack(Queue* q) { assert(q); assert(!QueueEmpty(q)); return q->tail->data; } // Gets the number of valid elements in the queue int QueueSize(Queue* q) { assert(q); int count = 0; QNode* cur = q->head; while (cur) { count++; cur = cur->next; } return count; } // Check whether the queue is empty. If it is empty, it returns a non-zero result. If it is not empty, it returns 0 int QueueEmpty(Queue* q) { assert(q); return q->head == NULL; }
summary
In general, the implementation of the linked list of queues is relatively easy. The "first in, first out" feature of queues can be used in some specific situations.