✨✨小新课堂开课了,欢迎欢迎~✨✨
🎈🎈养成好习惯,先赞后看哦~🎈🎈
所属专栏:数据结构与算法
小新的主页:编程版小新-CSDN博客
1.队列的定义
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表。队列严格遵循先进先出FIFO(First In First Out)的原则。
入队列:进行插入操作的一端称为队尾
出队列:进行删除操作的一端称为队头
2.队列的分类
队列的实现其实就和前面学习的栈一样,分为单链表实现实现的链式队,和数组实现的循环队。他们各有好处,也有需要细节需要注意。
1.链式队
我们用两个指针控制这个链表的头和尾,就能够实现队列的相关功能,为了方便计算队列的大小,我们也可以添加一个size来记录队列的大小。
2.循环队
我们在用数组实现栈的时候,使用的是一般的数组,我们用两个变量记录数组的起始位置和结束位置,但是一般的数组有能解决问题吗?如果不能存在什么问题,以及我们该怎么解决呢?接下来让我们一起去探索吧。
由此可以观察到,当在不断入队和出队后,前面的空间就会被浪费,不能在被使用。
为了解决这个问题我们可以使用循环数组。
循环数组虽然解决了上面的空间浪费的问题,但由此又产生了一个新的问题,我们该如何计算队列的大小呢?当head和tail指向同一个位置时队列为空还是为满呢?
第一种:牺牲一个单元来区分队空和队满,每当入队时tail++,当tail指向的下一个位置是head时即tail+1==head时,此时队列已满。每当出队时head++,当head==tail时,表示队列为空。
第二种:增添一个记录队列大小的变量size 。这样,队空的条件为 size==0;队满的条件为size==MaxSize。
3.队列的功能
1.队列的初始化
2.判断队列是否为空
3.判断队列是否为满
4.入队
5.出队
6.返回队头和队尾的数据
7.返回队列的大小
8.打印队列
9.销毁队列
4.队列的声明
1.链式队
//队列的声明
typedef int QDataType;
typedef struct QueueNode
{
QDataType x;
struct QueueNode* next;
}QueueNode;
typedef struct QueueNode* PQueueNode;
typedef struct Queue
{
PQueueNode head;
PQueueNode tail;
int size;
}Queue;
2.循环队
//队列的声明
typedef int QDataType;
#define MAXSIZE 50
typedef struct Queue
{
QDataType* data;
int head;
int tail;
}Queue;
5.功能实现
5.1队列的初始化
1.链式队
//队列的初始化
void QueueInit(Queue* pst)
{
pst->head = NULL;
pst->tail = NULL;
pst->size = 0;
}
2.循环队
//队列的初始化
void QueueInit(Queue* pst)
{
assert(pst);
pst->data = (QDataType*)malloc(sizeof(QDataType)*MAXSIZE);
if (pst->data == NULL)
{
perror("malloc fail");
return;
}
pst->head = 0;
pst->tail = 0;
}
5.2判断队列是否为空
1.链式队
//判断队列是否为空
bool QueueEmpty(Queue* pst)
{
assert(pst);
return pst->size == 0;
}
2.循环队
进行取模操作是为了防止越界访问,实现数组的回绕,即实现循环的功能。
//判断队列是否为空
bool QueueEmpty(Queue* pst)
{
assert(pst);
return (pst->head % MAXSIZE) == (pst->tail % MAXSIZE);
}
5.3判断队列是否为满
1.链式队
链式队是按需申请空间,不存在为满的情况。
2.循环队
按照我们上面分析的为满的情况实现即可,这里及其下面的取模操作都是为了防止越界访问。
//判断队列是否为满
bool QueueFull(Queue* pst)
{
assert(pst);
return ((pst->tail + 1) % MAXSIZE) == (pst->head % MAXSIZE);
}
5.4入队
1.链式队
//入队
void QueuePush(Queue* pst, QDataType x)
{
assert(pst);
PQueueNode newnode = (PQueueNode)malloc(sizeof(QueueNode));
newnode->next = NULL;
newnode->x = x;
if (newnode == NULL)
{
perror("malloc fail");
return;
}
if (QueueEmpty(pst))
{
pst->head = pst->tail = newnode;
}
else
{
pst->tail->next = newnode;
pst->tail = newnode;
}
pst->size++;
}
2.循环队
插入前要判满。
//入队
void QueuePush(Queue* pst, QDataType x)
{
assert(pst);
assert(!QueueFull(pst));
pst->data[pst->tail] = x;
pst->tail = (pst->tail + 1) % MAXSIZE;
}
5.5出队
1.链式队
在出队的时候分为两种情况,只有一个节点时和多个节点时,要进行分开处理。
//出队
void QueuePop(Queue* pst)
{
assert(pst);
assert(!QueueEmpty(pst));
//1.只有一个节点
if (pst->head == pst->tail)
{
free(pst->head);
pst->head = pst->tail = NULL;
}
else
{
//2.有多个节点
PQueueNode del = pst->head;
pst->head = pst->head->next;
free(del);
del = NULL;
}
pst->size--;
}
2.循环队
//出队
void QueuePop(Queue* pst)
{
assert(pst);
assert(!QueueEmpty(pst));
pst->head = (pst->head + 1) % MAXSIZE;
}
5.6返回队头和队尾的数据
1、链式队
//返回对头的数据
QDataType QueueFront(Queue* pst)
{
assert(pst);
assert(!QueueEmpty(pst));
return pst->head->x;
}
//返回队尾数据
QDataType QueueBack(Queue* pst)
{
assert(pst);
assert(!QueueEmpty(pst));
return pst->tail->x;
}
2.循环队
在返回队尾数据时,因为我们是没入一次队pst->tail++,所以在返回队尾数据时,pst->tail要先减去1,但是这里有可能在不断入队出队后,pst->tail=0,减1的话就会越界,而(pst->tail-1+MAXSIZE) % MAXSIZE就避免了这个问题。
//返回对头的数据
QDataType QueueFront(Queue* pst)
{
assert(pst);
assert(!QueueEmpty(pst));
return pst->data[pst->head%MAXSIZE];
}
//返回队尾数据
QDataType QueueBack(Queue* pst)
{
assert(pst);
assert(!QueueEmpty(pst));
return pst->data[(pst->tail-1+MAXSIZE) % MAXSIZE];
}
5.7返回队列的大小
1.链式队
//返回队列的大小
int QueueSize(Queue* pst)
{
assert(pst);
return pst->size;
}
2.循环队
//返回队列的大小
int QueueSize(Queue* pst)
{
return (pst->tail + MAXSIZE- pst->head) % MAXSIZE;
}
5.8打印队列
1.链式队
//打印队列的元素
void QueuePrint(Queue* pst)
{
assert(pst);
assert(pst->size > 0);
PQueueNode cur = pst->head;
printf("队头->");
while (cur != pst->tail->next)
{
printf("%d->", cur->x);
cur = cur->next;
}
printf("队尾\n");
}
2.循环队
//打印队列的元素
void QueuePrint(Queue* pst)
{
assert(pst);
assert(!QueueEmpty(pst));
int cur = pst->head;
printf("对头->");
while (cur!=pst->tail)
{
printf("%d->", pst->data[cur]);
cur = (cur + 1) % MAXSIZE;
}
printf("队尾\n");
}
5.9销毁队列
1.链式队
因为链式队时一个个节点来实现的,在销毁队列时就要一个一个去释放。
//销毁队列
void QueueDestroy(Queue* pst)
{
assert(pst);
PQueueNode cur = pst->head;
while (cur != NULL)
{
PQueueNode del = cur;
cur = cur->next;
free(del);
del = NULL;
}
pst->head = pst->tail = NULL;
pst->size = 0;
}
2.循环队
只要将整体开辟的一个空间释放即可。
//销毁队列
void QueueDestroy(Queue* pst)
{
assert(!QueueEmpty(pst));
free(pst->data);
pst->data = NULL;
pst->head = pst->tail = 0;
}
6.链式队和循环队的对比
7.队列的实际应用
1.吃饭排队取号
2.好友推荐也有队列的身影
8.完整代码
1.链式队
Queue.h
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
//链式队
//队列的声明
typedef int QDataType;
typedef struct QueueNode
{
QDataType x;
struct QueueNode* next;
}QueueNode;
typedef struct QueueNode* PQueueNode;
typedef struct Queue
{
PQueueNode head;
PQueueNode tail;
int size;
}Queue;
//队列的初始化
void QueueInit(Queue* pst);
//判断队列是否为空
bool QueueEmpty(Queue* pst);
//判断队列是否为满
//bool QueueFull(Queue* pst);
//入队
void QueuePush(Queue* pst, QDataType x);
//出队
void QueuePop(Queue* pst);
//返回对头的数据
QDataType QueueFront(Queue* pst);
//返回队尾数据
QDataType QueueBack(Queue* pst);
//返回队列的大小
int QueueSize(Queue* pst);
//打印队列的元素
void QueuePrint(Queue* pst);
//销毁队列
void QueueDestroy(Queue* pst);
Queue.c
#include"Queue.h"
//队列的初始化
void QueueInit(Queue* pst)
{
pst->head = NULL;
pst->tail = NULL;
pst->size = 0;
}
//判断队列是否为空
bool QueueEmpty(Queue* pst)
{
assert(pst);
return pst->size == 0;
}
//判断队列是否为满
//链式队不存在判满的情况
//入队
void QueuePush(Queue* pst, QDataType x)
{
assert(pst);
PQueueNode newnode = (PQueueNode)malloc(sizeof(QueueNode));
newnode->next = NULL;
newnode->x = x;
if (newnode == NULL)
{
perror("malloc fail");
return;
}
if (QueueEmpty(pst))
{
pst->head = pst->tail = newnode;
}
else
{
pst->tail->next = newnode;
pst->tail = newnode;
}
pst->size++;
}
//出队
void QueuePop(Queue* pst)
{
assert(pst);
assert(!QueueEmpty(pst));
//1.只有一个节点
if (pst->head == pst->tail)
{
free(pst->head);
pst->head = pst->tail = NULL;
}
else
{
//2.有多个节点
PQueueNode del = pst->head;
pst->head = pst->head->next;
free(del);
del = NULL;
}
pst->size--;
}
//返回对头的数据
QDataType QueueFront(Queue* pst)
{
assert(pst);
assert(!QueueEmpty(pst));
return pst->head->x;
}
//返回队尾数据
QDataType QueueBack(Queue* pst)
{
assert(pst);
assert(!QueueEmpty(pst));
return pst->tail->x;
}
//返回队列的大小
int QueueSize(Queue* pst)
{
assert(pst);
return pst->size;
}
//打印队列的元素
void QueuePrint(Queue* pst)
{
assert(pst);
assert(pst->size > 0);
PQueueNode cur = pst->head;
printf("队头->");
while (cur != pst->tail->next)
{
printf("%d->", cur->x);
cur = cur->next;
}
printf("队尾\n");
}
//销毁队列
void QueueDestroy(Queue* pst)
{
assert(pst);
PQueueNode cur = pst->head;
while (cur != NULL)
{
PQueueNode del = cur;
cur = cur->next;
free(del);
del = NULL;
}
pst->head = pst->tail = NULL;
pst->size = 0;
}
2.循环队
Queue.h
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
//队列的声明
typedef int QDataType;
#define MAXSIZE 50
typedef struct Queue
{
QDataType* data;
int head;
int tail;
}Queue;
//队列的初始化
void QueueInit(Queue* pst);
//判断队列是否为空
bool QueueEmpty(Queue* pst);
//判断队列是否为满
bool QueueFull(Queue* pst);
//入队
void QueuePush(Queue* pst, QDataType x);
//出队
void QueuePop(Queue* pst);
//返回对头的数据
QDataType QueueFront(Queue* pst);
//返回队尾数据
QDataType QueueBack(Queue* pst);
//返回队列的大小
int QueueSize(Queue* pst);
//打印队列的元素
void QueuePrint(Queue* pst);
//销毁队列
void QueueDestroy(Queue* pst);
Queue.c
#include"Queue.h"
//循环队(基于数组实现)
//队列的初始化
void QueueInit(Queue* pst)
{
assert(pst);
pst->data = (QDataType*)malloc(sizeof(QDataType)*MAXSIZE);
if (pst->data == NULL)
{
perror("malloc fail");
return;
}
pst->head = 0;
pst->tail = 0;
}
//判断队列是否为空
bool QueueEmpty(Queue* pst)
{
assert(pst);
return (pst->head % MAXSIZE) == (pst->tail % MAXSIZE);
}
//判断队列是否为满
bool QueueFull(Queue* pst)
{
assert(pst);
return ((pst->tail + 1) % MAXSIZE) == (pst->head % MAXSIZE);
}
//入队
void QueuePush(Queue* pst, QDataType x)
{
assert(pst);
assert(!QueueFull(pst));
pst->data[pst->tail] = x;
pst->tail = (pst->tail + 1) % MAXSIZE;
}
//出队
void QueuePop(Queue* pst)
{
assert(pst);
assert(!QueueEmpty(pst));
pst->head = (pst->head + 1) % MAXSIZE;
}
//返回对头的数据
QDataType QueueFront(Queue* pst)
{
assert(pst);
assert(!QueueEmpty(pst));
return pst->data[pst->head%MAXSIZE];
}
//返回队尾数据
QDataType QueueBack(Queue* pst)
{
assert(pst);
assert(!QueueEmpty(pst));
return pst->data[(pst->tail-1+MAXSIZE) % MAXSIZE];
}
//返回队列的大小
int QueueSize(Queue* pst)
{
return (pst->tail + MAXSIZE- pst->head) % MAXSIZE;
}
//打印队列的元素
void QueuePrint(Queue* pst)
{
assert(pst);
assert(!QueueEmpty(pst));
int cur = pst->head;
printf("对头->");
while (cur!=pst->tail)
{
printf("%d->", pst->data[cur]);
cur = (cur + 1) % MAXSIZE;
}
printf("队尾\n");
}
//销毁队列
void QueueDestroy(Queue* pst)
{
assert(!QueueEmpty(pst));
free(pst->data);
pst->data = NULL;
pst->head = pst->tail = 0;
}