Data Structure Part Five: Queue

foreword

  I learned about the stack earlier, which is characterized by last-in-first-out. The queue explained today is just the opposite. It is first-in, first-out, and the first-in data goes out first.

1. queue

1.1 The concept and structure of the queue

  Queue: A special linear table that only allows inserting data at one end and deleting data at the other end. The queue has a first-in-first-out FIFO (First In First Out) queue: the end where the
  inserting operation is performed is called the tail of the queue.
  Out of the queue: The end of the delete operation is called the head of the queue.
insert image description here

1.2 Implementation of the queue

  Queues can also be implemented in the structure of arrays and linked lists. It is better to use the structure of linked lists, because if you use the structure of arrays, the efficiency of dequeueing and outputting data at the head of the array will be relatively low.
insert image description here

  1. front == NULL, tail == NULL indicates that the queue is empty.
  2. When inserting the first element, front and tail point to the first element at the same time.
  3. When entering the queue, it is the tail insertion of the singly linked list.
  4. When leaving the queue, the head of the singly linked list is deleted.

2. Analysis and realization of each function

2.1 Creation of queues

  We use a linked list to implement it, because if an array is used, all elements need to be moved forward by one bit when dequeuing. In fact, the first element is covered, which is more cumbersome and less efficient than a linked list. , so it is implemented here in the form of a linked list.

typedef int Datatype;
//链式结构表示队列
typedef struct QListNode
{
    
    
	Datatype data;
	struct Queue* next;
}QNode;

typedef struct Queue
{
    
    
	QNode* front;
	QNode* tail;
}Queue;

  We define two structures, one is the content of each element, a data field and a pointer field. The other is a pointer to the element. Because two are needed in the queue, it is better to encapsulate them directly. At the same time encapsulated in a structure, we can just pass the pointer of the structure by pointer, and there is no need to pass a single (separate front or tail) secondary pointer. (If you don't quite understand , you can compare the subsequent code with the code in the single-linked list blog, and you can better understand why the first-level pointer is passed here, while the second-level pointer is passed in the single-linked list.

2.2 Initialize the queue

  Just set the pointer to empty to prevent the wild pointer problem.

void QueueInit(Queue* pq)
{
    
    
	assert(pq);
	pq->front = NULL;
	pq->tail = NULL;
}

2.3 The end of the queue enters the queue

  Every time it enters the queue, a new space is opened to store the elements, and then the tail insertion is completed. It should be noted that when inserting the first element, the address of the new node is given to the front and tail instead of tail insertion. Therefore, the front and tail at this time are null pointers, and they cannot be dereferenced.

void QueuePush(Queue* pq, Datatype x)
{
    
    
	assert(pq);	
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
    
    
		printf("开辟失败!!");
		return;
	}
	newnode->next = NULL;
	newnode->data = x;
	
	if (pq->front == NULL)
	{
    
    
		pq->tail = newnode;
		pq->front = newnode;
	}
	else
	{
    
    
		pq->tail->next = newnode;
		pq->tail = pq->tail->next;
	}
}

2.4 Dequeue from the head of the queue

  To delete the head element, we only need to save the address of the second element, then release the space of the head element, and then move the front to the position of the second element. It should be noted that when there is only one node left in the queue, front and tail point to the same space. When the front is released, tail will also be released, as shown in the figure: at this time, tail is a wild pointer pointing
insert image description here  to A piece of space has been released, so we need to add a judgment, that is, when front == NULL, tail should also be set to NULL.

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

	QNode* next = pq->front->next;
	free(pq->front);
	pq->front = next;
	if (pq->front == NULL)
	{
    
    
		pq->tail = NULL;
	}
}

2.5 Get the element at the head of the queue

  The only thing to pay attention to is to judge whether the queue is empty.

Datatype QueueFront(Queue* pq)
{
    
    
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->front->data;
}

2.6 Get the elements at the end of the queue

  The only thing to pay attention to is to judge whether the queue is empty.

Datatype QueueBack(Queue* pq)
{
    
    
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->tail->data;
}

2.7 The number of valid elements in the queue

  It can be calculated sequentially from the beginning.

int QueueSize(Queue* pq)
{
    
    
	assert(pq);
	int count = 0;
	QNode* cur = pq->front;
	while (cur)
	{
    
    
		count++;
		cur = cur->next;
	}
	return count;
}

2.8 Check if the queue is empty

  The state of the queue being empty is that the front is a null pointer and does not point to an element.

bool QueueEmpty(Queue* pq)
{
    
    
	assert(pq);
	if (pq->front == NULL)
	{
    
    
		return true;
	}
	return false;
}

2.9 Destroy the queue

  You only need to save the next node address of the node to be released in advance, and then continue to release it in a loop.

void QueueDestroy(Queue* pq)
{
    
    
	assert(pq);
	QNode* cur = pq->front;
	while (cur != NULL)
	{
    
    
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->front = NULL;
	pq->tail = NULL;
}

3. Code implementation

3.1 Queue.h

  Contains the creation of the queue and the declaration of each function.

#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>

typedef int Datatype;

//链式结构表示队列
typedef struct QListNode
{
    
    
	Datatype data;
	struct Queue* next;
}QNode;

typedef struct Queue
{
    
    
	QNode* front;
	QNode* tail;
}Queue;

// 初始化队列
void QueueInit(Queue* pq);

// 队尾入队列
void QueuePush(Queue* pq, Datatype data);

// 队头出队列
void QueuePop(Queue* pq);

// 获取队列头部元素
Datatype QueueFront(Queue* pq);

// 获取队列队尾元素
Datatype QueueBack(Queue* pq);

// 获取队列中有效元素个数
int QueueSize(Queue* pq);

// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
bool QueueEmpty(Queue* pq);

// 销毁队列
void QueueDestroy(Queue* pq);

3.2 Queue.c

  The realization of each functional function.

#include"Queue.h"

void QueueInit(Queue* pq)
{
    
    
	assert(pq);
	
	pq->front = NULL;
	pq->tail = NULL;
}

void QueueDestroy(Queue* pq)
{
    
    
	assert(pq);
	QNode* cur = pq->front;
	while (cur != NULL)
	{
    
    
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->front = NULL;
	pq->tail = NULL;
}

void QueuePush(Queue* pq, Datatype x)
{
    
    
	assert(pq);	
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
    
    
		printf("开辟失败!!");
		return;
	}
	newnode->next = NULL;
	newnode->data = x;
	
	if (pq->front == NULL)
	{
    
    
		pq->tail = newnode;
		pq->front = newnode;
	}
	else
	{
    
    
		pq->tail->next = newnode;
		pq->tail = pq->tail->next;
	}
}

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

	QNode* next = pq->front->next;
	free(pq->front);
	pq->front = next;
	if (pq->front == NULL)
	{
    
    
		pq->tail = NULL;
	}
}


Datatype QueueFront(Queue* pq)
{
    
    
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->front->data;
}

Datatype QueueBack(Queue* pq)
{
    
    
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->tail->data;
}

int QueueSize(Queue* pq)
{
    
    
	assert(pq);
	int count = 0;
	QNode* cur = pq->front;
	while (cur)
	{
    
    
		count++;
		cur = cur->next;
	}
	return count;
}

bool QueueEmpty(Queue* pq)
{
    
    
	assert(pq);
	if (pq->front == NULL)
	{
    
    
		return true;
	}
	return false;
}

3.3 test.c

  A test of the queue functionality.

#include"Queue.h"

void test()
{
    
    
	Queue q;
	QueueInit(&q);

	QueuePush(&q, 1);//测试:入队列
	QueuePush(&q, 2);
	QueuePush(&q, 3);
	QueuePush(&q, 4);

	printf("%d\n",QueueFront(&q));//测试:获取队头元素

	printf("%d\n", QueueSize(&q));//测试:统计元素个数

	QueueDestroy(&q);
}

void test1()
{
    
    
	Queue q;
	QueueInit(&q);

	QueuePush(&q, 1);//测试:入队列
	QueuePush(&q, 2);
	QueuePush(&q, 3);
	QueuePush(&q, 4);

	while (!QueueEmpty(&q))
	{
    
    
		Datatype x = QueueFront(&q);
		printf("%d ", x);
		QueuePop(&q);   //测试:出队列
	}
	printf("\n");

	QueueDestroy(&q);
}
int main()
{
    
    
	test();
	//test1();
	return 0;
}

4. Summary

  Similar to a stack, one is implemented with an array, the other is implemented with a linked list, one is last in first out, and the other is first in first out. There is not much difference in difficulty, and the focus is still on doing the questions and I feel better.
  Then the explanation of the queue will come to an end first. If you find any problems in the article, you can raise them in the comment area or private message me. Next, I will continue to learn other knowledge of data structure in depth and start a new chapter, so this issue ends here, let's see you in the next issue! ! If you feel good, you can click a like to show your encouragement! !

Guess you like

Origin blog.csdn.net/qq_62321047/article/details/130464544