<Data structure> Implementation of queue

content

foreword

        queue concept

        The structure of the queue

        Queue application scenarios

Implementation of the queue

        Create queue structure

        queue initialization

        Queue destruction

        enqueue

        dequeue

        Queue is empty

        Get the number of queue elements

        Get the queue head element

        Get the element at the end of the queue

total code

        Queue.h file

        Queue.c file

        Test.c file


foreword

queue concept

  • Queue: A special linear table that only allows data insertion operations at one end and deletion data operations at the other end. The queue has a First In First Out (First In First Out)
  • Enqueue: The end that performs the insertion operation is called the tail of the queue
  • Dequeue: The end that performs the delete operation is called the head of the queue

There is still a certain difference between the queue and the stack learned in the previous article. The queue clearly points out the first in, first out. If the order of entry of a queue is ABCD, then the order of dequeuing must be ABCD, because whether you enter in A and then come out, then B in and out, then CD in and out or similar, it will not affect its final Dequeue order ABCD. This is still different from the stack, after all, the stack is LIFO.

The structure of the queue

Queue application scenarios

queue:

  1. fair queue
  2. Breadth-first traversal...

stack:

  1. resolve bracket matching
  2. Reverse Polish Expression Solver
  3. Recursive to non-recursive...

Implementation of the queue

  • Before implementing, we must first consider which structure to use, whether to use an array structure or a chain structure? The stack we used above is an array structure, should we also use the queue?
  • actually not. A chained structure should be used. The previous stack deletes data without moving the data, the array structure can be used to meet the needs, and the queue needs to move the following data to the front when deleting data. It is very easy to use the chain structure, just change the node point, and the array It is very troublesome for the structure to realize moving data. In summary, the use of a chain structure is optimal. In addition, a singly linked list can meet the requirements, and there is no need to use other more complex chain structures.

Create queue structure

  •  Ideas:

Here to define two structures, in addition to defining a chain structure to record each node, but also to define a structure to record the head and tail of the queue. In this way, it is convenient for the subsequent tail of the queue to input data and the head of the queue to output data.

  • Queue.h file:
//创建队列结构
typedef int QDataType; //方便后续更改存储数据类型,本文以int为例
 //创建队列节点
typedef struct QueueNode
{
	QDataType data; //存储数据
	struct QueueNode* next; //记录下一个节点
}QNode;
 //保存队头和队尾
typedef struct Queue
{
	QNode* head; //头指针
	QNode* tail; //尾指针
}Queue;

queue initialization

  •  Ideas:

The queue can be empty, but the structure that manages the head and tail pointers cannot be empty, so it must be asserted at the beginning. Secondly, before inserting data, the queue must be empty, so just empty the head pointer and tail pointer.

  • Queue.h file:
//初始化队列
void QueueInit(Queue* pq);
  • Queue.c file:
//初始化队列
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->head = pq->tail = NULL;
}

Queue destruction

  •  Ideas:

Destroying the queue is to destroy every data in the queue, then you need to traverse the linked list to destroy free one by one. First define a cur pointer as pq->head, which is used to save the first data, traverse cur, and if it is not empty, free. Finally, leave the tail and head empty.

  • Queue.h file:
//销毁队列
void QueueDestory(Queue* pq);
  • Queue.c file:
//销毁队列
void QueueDestory(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->head = pq->tail = NULL;
}

enqueue

  •  Ideas:

Entering the queue is actually very simple. You only need tail insertion. First, you need to create a new node to save the newly inserted data. But before the tail insertion, it should be considered that if the queue has no data at the beginning and is empty, then you only need to point the head and tail nodes to the new node newnode node. On the contrary, if there is data at the beginning, then it is only necessary to point the next of the tail to the new node newnode, and then assign the newnode to the tail.

  • Queue.h file:
//入队列
void QueuePush(Queue* pq, QDataType x);
  •  Queue.c file:
//入队列
void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	//创建一个新节点保存数据
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	//暴力检测newnode,因为malloc的都要检测
	assert(newnode);
	newnode->next = NULL;
	newnode->data = x;
	//如果一开始没有数据,为空的情况
	if (pq->tail == NULL)
	{
		assert(pq->head == NULL);
		pq->head = pq->tail = newnode;
	}
	else
	{
		pq->tail->next = newnode;
		pq->tail = newnode;
	}
}

dequeue

  •  Ideas:

Special cases:

Here, when deleting data, we must first consider special cases. When only one data is left, delete it again. At this time, the data is gone, but the head is empty, and the tail becomes a wild pointer. In order to avoid this phenomenon The generation of , discussed separately and empty head and tail.

generally:

At this point, you only need to define a next pointer to save the next node of the head, move the head to the next, and empty the old head.

  •  Queue.h file:
//出队列
void QueuePop(Queue* pq);
  • Queue.c file:
//出队列
void QueuePop(Queue* pq)
{
	assert(pq);
	assert(pq->head && pq->tail); //tail和head均不能为空
	//特殊:当删到head=tail的位置时
	if (pq->head->next == NULL)
	{
		free(pq->head);
		pq->head = pq->tail = NULL;
	}
	//一般情况
	else
	{
		//保存head的下一个节点
		QNode* next = pq->head->next;
		free(pq->head);
		pq->head = next;
	}
}

Queue is empty

  •  Ideas:

If the head is empty or the tail is empty, it is an empty condition, just return directly.

  • Queue.h file:
//判空
bool QueueEmpty(Queue* pq);
  • Queue.c file:
//判空
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->head == NULL;
}

Get the number of queue elements

  •  Ideas:

It is not difficult to find the number of elements, just define a cur pointer as the first data pq->head, and define the variable size to record the number. Traverse cur in turn, if it is not empty, size is ++. The idea of ​​​​this traversal is not complicated, but the time complexity reaches O(N), which is not very good. If you want O(1), you can directly define an additional size variable when you define the structure at the beginning, which is specially used to record the number of valid elements. Number, each time into the queue size++, out of the queue size--. This implementation is better, but in order to encapsulate it into an independent module, the traversal method is still used. as follows:

  • Queue.h file:
//获取有效元素个数
size_t QueueSize(Queue* pq);
  • Queue.c file:
//获取有效元素个数
size_t QueueSize(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	size_t size = 0;
	while (cur)
	{
		size++;
		cur = cur->next;
	}
	return size;
}

Get the queue head element

  •  Ideas:

First of all, it is necessary to assert that the head cannot be empty. If the head is empty, how can we get the head element, and then directly return the data of the head.

  • Queue.h file:
//获取队头元素
QDataType QueueFront(Queue* pq);
  • Queue.c file:
//获取队头元素
QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->head); //头部不能为空
	return pq->head->data;
}

Get the element at the end of the queue

  •  Ideas:

With the experience of obtaining the elements of the head of the queue, the tail of the queue is simpler, just change the head to the tail, and the structure is the same as above.

  • Queue.h file:
//获取队尾元素
QDataType QueueBack(Queue* pq);
  • Queue.c file:
//获取队尾元素
QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(pq->tail); //尾部不能为空
	return pq->tail->data;
}

total code

Queue.h file

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

//创建队列结构
typedef int QDataType; //方便后续更改存储数据类型,本文以int为例
 //创建队列节点
typedef struct QueueNode
{
	QDataType data; //存储数据
	struct QueueNode* next; //记录下一个节点
}QNode;
 //保存队头和队尾
typedef struct Queue
{
	QNode* head; //头指针
	QNode* tail; //尾指针
}Queue;

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

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

//入队列
void QueuePush(Queue* pq, QDataType x);

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

//判空
bool QueueEmpty(Queue* pq);

//获取有效元素个数
size_t QueueSize(Queue* pq);

//获取队头元素
QDataType QueueFront(Queue* pq);

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

Queue.c file

#define _CRT_SECURE_NO_WARNINGS 1
#include"Queue.h"

//初始化队列
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->head = pq->tail = NULL;
}

//销毁队列
void QueueDestory(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->head = pq->tail = NULL;
}

//入队列
void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	//创建一个新节点保存数据
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	//暴力检测newnode,因为malloc的都要检测
	assert(newnode);
	newnode->next = NULL;
	newnode->data = x;
	//如果一开始没有数据,为空的情况
	if (pq->tail == NULL)
	{
		assert(pq->head == NULL);
		pq->head = pq->tail = newnode;
	}
	else
	{
		pq->tail->next = newnode;
		pq->tail = newnode;
	}
}

//出队列
void QueuePop(Queue* pq)
{
	assert(pq);
	assert(pq->head && pq->tail); //tail和head均不能为空
	//特殊:当删到head=tail的位置时
	if (pq->head->next == NULL)
	{
		free(pq->head);
		pq->head = pq->tail = NULL;
	}
	//一般情况
	else
	{
		//保存head的下一个节点
		QNode* next = pq->head->next;
		free(pq->head);
		pq->head = next;
	}
}

//判空
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->head == NULL;
}

//获取有效元素个数
size_t QueueSize(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	size_t size = 0;
	while (cur)
	{
		size++;
		cur = cur->next;
	}
	return size;
}

//获取队头元素
QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->head); //头部不能为空
	return pq->head->data;
}

//获取队尾元素
QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(pq->tail); //尾部不能为空
	return pq->tail->data;
}

Test.c file

#define _CRT_SECURE_NO_WARNINGS 1
#include"Queue.h"
void TestQueue()
{
	Queue q;
	QueueInit(&q);
	//插入数据
	QueuePush(&q, 1);
	QueuePush(&q, 2);
	QueuePush(&q, 3);
	QueuePush(&q, 4);
	//打印
	while (!QueueEmpty(&q))
	{
		printf("%d ", QueueFront(&q));
		QueuePop(&q);
	}
	printf("\n");
}
int main()
{
	TestQueue();
	return 0;
}

Guess you like

Origin blog.csdn.net/bit_zyx/article/details/123967787