Data structure - the implementation of the queue (details are finished)

1. queue

1.1 The concept and structure of the queue

If the queue we implement today is completely opposite, the queue is data first in first out . In the stack, we use the sequence table (array) to achieve it. It is more appropriate to implement the queue with a singly linked list .

Queue: A special linear table that only allows inserting data at one end and performing data operations at the other end. The queue has a first-in-first-out FIFO (Frist in First out).

Into the queue: the end of the insertion operation is called the tail of the queue
Out of the queue: the end of the deletion operation is called the head of the queue

 Here we give an example: when you buy food in our cafeteria, the merchant will give you a number after you pay, and you can go to pick up the meal when your number is called, so as not to be cut in line, the first to buy will be called first Arrival number, and the queue is based on such a principle, the nature of first-in-first-out.

 Since the queue needs to perform tail plug and delete operations, we can define another structure to save the head pointer and tail pointer of the queue. It is best to save the size of the queue as well. Because when we want to enter the queue and exit the queue, we only need to use the head pointer and tail pointer of the queue to perform the tail plug deletion operation.


The following code explains:

typedef int QDataType;
typedef struct QueueNode
{
	struct QueueNode* next;
	QDataType data;
}QNode;
typedef struct Queue
{
	QNode* phead;
	QNode* ptail;
	int size;
}Queue;

void QueueInit(Queue* pq);
void QueueDestroy(Queue* pq);
void QueuePush(Queue* pq, QDataType x);
void QueuePop(Queue* pq);
QDataType QueueFront(Queue* pq);
QDataType QueueBack(Queue* pq);
int QueueSize(Queue* pq);
bool QueueEmpty(Queue* pq);

2.1 Initialize and destroy queues

The singly linked list we implemented here is a single linked list with no head and no loop . So we initialize the queue head and queue tail as null pointers.

The destruction of the queue is the same as what we have learned before. Each time the position of the head node is recorded, and the head node is released until it is empty.

void QueueInit(Queue* pq)
{
	assert(pq);
	pq->phead = NULL;
	pq->ptail = NULL;
	pq->size = 0;
}

void QueueDestroy(Queue* pq)
{
	assert(pq);

	QNode* cur = pq->phead;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

2.2 Tail entry of the queue

Here we have two cases:

The first type : when the data is tailed for the first time, the new node is the tail of the queue and the head of the queue, and the new node is assigned to the head of the queue and the tail of the queue .

The second type : the queue already has data, we link the new node to the tail, and then assign the new node as a new node, that is, update the tail of the queue. (that is, the tail plug)

Summary: The head of the queue remains unchanged. If a piece of data is entered at the end of the queue, the end of the queue needs to be moved one bit to the back.

void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);

	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newnode->data = x;
	newnode->next = NULL;

	if (pq->ptail == NULL)
	{
		assert(pq->phead == NULL);
			pq->phead = pq->ptail = newnode;
	}
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}
	pq->size++;
}

2.3 Dequeue

Here we have two cases:

  • When there is only one node in the queue , just free the first node directly , and then assign the queue head and the end of the queue to be empty.
  • When the queue has two or more nodes, we first save the next node at the head of the queue, and then free the head of the queue .

Note that the size should be increased by 1

void QueuePop(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));


	//1.一个节点
	//2.多个节点
	if (pq->phead->next == NULL)
	{
		free(pq->phead);
		pq->phead = pq->ptail = NULL;
	}
	else
	{
		//头删
		QNode* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;
	}
	pq->size--;
}

2.4 Get the head element and tail element of the queue

Here it is very simple to directly return to our head, and the tail pointer points to the data stored in the node.

QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	return pq->phead->data;
}

QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	return pq->ptail->data;
}

2.5 Determine whether the queue is empty and obtain the number of queue elements

To judge whether the queue is empty, you only need to check whether its size is 0 , or whether the head pointer and tail pointer of the queue are both null pointers.

bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->size == 0;
}

It is very simple to get the number of elements in the queue, just return the size of the queue

int QueueSize(Queue* pq)
{
	assert(pq);
	return pq->size;
}

Queue.h

typedef int QDataType;
typedef struct QueueNode
{
	struct QueueNode* next;
	QDataType data;
}QNode;
typedef struct Queue
{
	QNode* phead;
	QNode* ptail;
	int size;
}Queue;

void QueueInit(Queue* pq);
void QueueDestroy(Queue* pq);
void QueuePush(Queue* pq, QDataType x);
void QueuePop(Queue* pq);
QDataType QueueFront(Queue* pq);
QDataType QueueBack(Queue* pq);
int QueueSize(Queue* pq);
bool QueueEmpty(Queue* pq);

Queue.c

#include "Queue.h"


void QueueInit(Queue* pq)
{
	assert(pq);
	pq->phead = NULL;
	pq->ptail = NULL;
	pq->size = 0;
}

void QueueDestroy(Queue* pq)
{
	assert(pq);

	QNode* cur = pq->phead;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);

	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newnode->data = x;
	newnode->next = NULL;

	if (pq->ptail == NULL)
	{
		assert(pq->phead == NULL);
			pq->phead = pq->ptail = newnode;
	}
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}
	pq->size++;
}

void QueuePop(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));


	//1.一个节点
	//2.多个节点
	if (pq->phead->next == NULL)
	{
		free(pq->phead);
		pq->phead = pq->ptail = NULL;
	}
	else
	{
		//头删
		QNode* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;
	}
	pq->size--;
}

QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	return pq->phead->data;
}

QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	return pq->ptail->data;
}

int QueueSize(Queue* pq)
{
	assert(pq);
	return pq->size;
}

bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->size == 0;
}

test.c

#include "Queue.h"

void TestQueue()
{
	Queue q;
	QueueInit(&q);
	QueuePush(&q, 1);
	QueuePush(&q, 2);
	QueuePush(&q, 3);
	QueuePush(&q, 4);
	QueuePush(&q, 5);

	while (!QueueEmpty(&q))
	{
		printf("%d ", QueueFront(&q));
		QueuePop(&q);
	}
	printf("\n");

	QueueDestroy(&q);
}

int main()
{
	TestQueue();
	return 0;
}

The above is all the content of today’s sharing queue. If you like it, please support it. See you next time

Guess you like

Origin blog.csdn.net/m0_74459304/article/details/130782367