新手数据结构精通——队列

发布于:2022-10-23 ⋅ 阅读:(406) ⋅ 点赞:(0)

新手数据结构精通——队列

1队列的概念及结构

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out) 入队列:进行插入操作的一端称为队尾 出队列:进行删除操作的一端称为队头。

理解:可以想象成一根吸管

在这里插入图片描述

2队列的设计

队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。

最初设置一个链表节点当然都是NULL,再把节点作为一个元素放到另一个结构体内,设置front和rear都指向这个节点。
在这里插入图片描述
插入一个节点,front指向这个节点的地址,rear指向这个节点的NEXT。
在这里插入图片描述
再插入一个元素,front就不用改动,改动的都是rear,每增加一个数据节点,rear就会往后移动并指向新节点。
在这里插入图片描述
增加多个节点之后的样子
在这里插入图片描述

重点注释

这里的增删需要解释一下,因为队列是先入先出的结构,那么我们要增加一个数字应用在链表上就是:头插尾删。明白这一点十分重要。

代码实现

先设计一个单链表的结构体(忘记了请翻看之前讲解的内容单链表精讲),然后再用链表节点构造出队列。


// 链式结构:表示队列
typedef int QDataType;
typedef struct QListNode
{
	struct QListNode* _pNext;
	QDataType _data;
}QNode;

// 队列的结构
typedef struct Queue
{
	QNode* _front;
	QNode* _rear;
}Queue;

初始化

其实就是和图像一致。第一步先将front和rear都置为NULL。
在这里插入图片描述
先做出一个队列 q。
在这里插入图片描述

// 初始化队列
void QueueInit(Queue* q)
{
	q->_front = q->_rear = NULL;
}

插入

这里的插入和我之前说过的一样,就是链表尾插。但是要做好对队列的判断。比如:空指针,没定义,空队列。

//头出尾入,front出 rear 入
// 队尾入队列
void QueuePush(Queue* q, QDataType data)
{
	assert(q);
	QNode* Newnode = (QNode*)malloc(sizeof(QNode));
	Newnode->_data = data;
	Newnode->_pNext = NULL;
	if (q->_front == NULL)
	{
		q->_front = q->_rear = Newnode;
	}
	else
	{
		q->_rear->_pNext = Newnode;
		q->_rear = Newnode;
		
	}
}

重点:头出尾入,front出 rear 入

出队

// 队头出队列
void QueuePop(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));//检验是否为空
	Queue* gone = q->_front;
	q->_front = q->_front->_pNext;
	free(gone);
	gone = NULL;
	if (q->_front == NULL)
	{
		q->_rear = NULL;
	}
}

一定要记得检验是否为空,所以我们还要设计一个检验空的函数。

// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* q)
{
	assert(q);
	return q->_front == NULL;
}

这里返回值用bool也可以的,但是因为效果一样我就不改了。如果用bool记得增加bool头函数。

查找有很多种方式。比如:头部数据、尾部数据、有多少元素。我们都实现以下。

// 获取队列头部元素
QDataType QueueFront(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));
	return q->_front->_data;
}
// 获取队列队尾元素
QDataType QueueBack(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));
	return q->_rear->_data;
}
// 获取队列中有效元素个数
int QueueSize(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));
	int count = 0;
	QNode* cur = q->_front;
	while (cur)
	{
		cur = cur->_pNext;
		++count;
	}
}

每个函数都要检验一下是否为空,因为有时候我们会写出野指针的。

销毁

学了数据结构一定要记得最后要销毁,否则一直占用堆,会影响性能,也不严谨。

// 销毁队列
void QueueDestroy(Queue* q)
{
	assert(q);
	QNode* cur = q->_front;
	while (cur != NULL)
	{
		QNode* next = cur->_pNext;
		free(cur);
		cur = next;
	}
	q->_front=q->_rear = NULL;

}

代码汇总

头文件

#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>

// 链式结构:表示队列
typedef int QDataType;
typedef struct QListNode
{
	struct QListNode* _pNext;
	QDataType _data;
}QNode;
// 队列的结构
typedef struct Queue
{
	QNode* _front;
	QNode* _rear;
}Queue;
// 初始化队列
void QueueInit(Queue* q);
// 队尾入队列
void QueuePush(Queue* q, QDataType data);
// 队头出队列
void QueuePop(Queue* q);
// 获取队列头部元素
QDataType QueueFront(Queue* q);
// 获取队列队尾元素
QDataType QueueBack(Queue* q);
// 获取队列中有效元素个数
int QueueSize(Queue* q);
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* q);
// 销毁队列
void QueueDestroy(Queue* q);

程序函数.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Queue.h"

// 初始化队列
void QueueInit(Queue* q)
{
	q->_front = q->_rear = NULL;
}

//头出尾入,front出 rear 入
// 队尾入队列
void QueuePush(Queue* q, QDataType data)
{
	assert(q);
	QNode* Newnode = (QNode*)malloc(sizeof(QNode));
	Newnode->_data = data;
	Newnode->_pNext = NULL;
	if (q->_front == NULL)
	{
		q->_front = q->_rear = Newnode;
	}
	else
	{
		q->_rear->_pNext = Newnode;
		q->_rear = Newnode;
		
	}
}
// 队头出队列
void QueuePop(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));//检验是否为空
	Queue* gone = q->_front;
	q->_front = q->_front->_pNext;
	free(gone);
	gone = NULL;
	if (q->_front == NULL)
	{
		q->_rear = NULL;
	}
}
// 获取队列头部元素
QDataType QueueFront(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));
	return q->_front->_data;
}
// 获取队列队尾元素
QDataType QueueBack(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));
	return q->_rear->_data;
}
// 获取队列中有效元素个数
int QueueSize(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));
	int count = 0;
	QNode* cur = q->_front;
	while (cur)
	{
		cur = cur->_pNext;
		++count;
	}
}
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* q)
{
	assert(q);
	return q->_front == NULL;
}
// 销毁队列
void QueueDestroy(Queue* q)
{
	assert(q);
	QNode* cur = q->_front;
	while (cur != NULL)
	{
		QNode* next = cur->_pNext;
		free(cur);
		cur = next;
	}
	q->_front=q->_rear = NULL;

}

主函数和测试函数

#define _CRT_SECURE_NO_WARNINGS 1
#include"Queue.h"

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

	//QueuePop(&q);
	/*QueuePop(&q);
	QueuePop(&q);
	QueuePop(&q);*/
	//QueuePop(&q);
	/*printf("%d ", QueueSize(&q));
	printf("%d ", QueueFront(&q));
	printf("%d ", QueueBack(&q));*/
	while (!QueueEmpty(&q))
	{
		QDataType front = QueueFront(&q);
		printf("%d ", front);
		QueuePop(&q);
	}
	printf("\n");
}
int main()
{
	TestFunc();
	return 0;
}

测试结果
在这里插入图片描述