队列
概念及结构
只允许在一端进行插入数据,在另一端进行删除数据操作的特殊线性表,队列具有先进先出的特性(First In First Out)
队尾:进行插入操作的一端
队头:进行删除操作的一端
结构示意图:
队列的代码实现
本篇就以单链表(不带哨兵位)为例来实现队列
队列结点的定义
typedef int QDataType;
typedef struct QueueNode
{
QDataType data;
struct QueueNode* next;
}QNode;
为了方便找到单链表的的尾结点,实现队列的插入数据,这里再定义一个结构体Que
typedef struct Queue
{
QNode* head;
QNode* tail;
int size;
}Que;
定义这样一个结构体的优点:
实现队列的插入和删除数据不需要传二级指针,只需要传Que
的结构体指针就可以对队列进行操作
队列的相关操作函数
void QueueInit(Que* pq); // 队列初始化函数
void QueueDestroy(Que* pq); // 队列销毁函数
void QueuePush(Que* pq, QDataType x); // 增加数据
void QueuePop(Que* pq); // 删除数据
QDataType QueueFront(Que* pq); // 取队头数据
QDataType QueueBack(Que* pq); // 取队对尾数据
bool QueueEmpty(Que* pq); // 判断队列是否为空
int QueueSize(Que* pq); // 返回队列数据个数
在这里着重说一下QueuePush
函数和QueuePop
函数
QueuePush():
void QueuePush(Que* pq, QDataType x) // 增加数据
{
assert(pq);
QNode* newNode = (QNode*)malloc(sizeof(QNode));
if (newNode == NULL)
{
perror("malloc fail");
exit(-1);
}
newNode->data = x; // 将要插入的数据
newNode->next = NULL; // 新建结点的next指针指向NULL
// 连接结点分两种情况
if (pq->tail == NULL) //队列中没有数据
{
pq->head = pq->tail = newNode;
}
else // 队列中有数据
{
pq->tail->next = newNode;
pq->tail = newNode;
}
pq->size++;
}
插入数据,先新建一个结点newNode
,然后链如队列里边,注意分两种情况:队列中没有数据和队列中有数据,可以用队列的tail
指针是否为空来进行判断,最后不要忘记处理队列中的size
变量的更新。
QueuePop():
void QueuePop(Que* pq) // 删除数据
{
assert(pq != NULL); // pq不能为空
assert(pq->head != NULL); // 队列中不能没有数据
if (pq->head == pq->tail) // 剩了一个结点,单独判断
{
free(pq->head);
pq->head = pq->tail = NULL;
}
else // 剩了大于一个结点
{
QNode* del = pq->head;
pq->head = pq->head->next;
free(del);
}
pq->size--;
}
删除队列数据,需要从队头进行删除,队列中需要有数据。删除队头数据需要分两种情况:
- 队列中只剩下一个数据,删除后不仅需要处理
pq->head
,也需要处理pq->tail
,否则pq->tail
是野指针。 - 队列中还有多个数据(大于一个),只需要对队列的尾结点释放即可,不用更改头指针
pq->head
。
最后都需要更新pq->size
。
循环队列
将队列的收尾相连从逻辑上看形成一个环,称为环形队列或者循环队列
逻辑图:
循环队列的实现可以使用数组也可以使用链表实现,不过使用链表有以下缺点:
- 对于单链表实现的队列,取队尾的数据不好取。
- 解决方法:可以用双向循环队列
- 不好区分队列是空还是满的状态。
- 解决方法:始终空一个数据的位置,
front==rear
就是空,front==rear->next
就是队列满
- 解决方法:始终空一个数据的位置,
下面就以数组方式来实现循环队列
始终多开一个空间,该队列最多可以存放4个数据,在这个却开辟5个数据空间,用来区分队列空、队列满的状态
在这个图片中可以看出front==rear
,此时为队列空。
上方这个就是队列满的状态 front==rear+1
,我们还要考虑当rear
在最后一个的时候的情况,也就是下图:
此时队列满的条件就是(rear+1)%MAX==front
,这里的MAX
是数组的长度5
循环队列结构体
#define MAX 5
typedef int QDataType;
typedef struct CircularQueue
{
QDataType* a;
int front;
int rear;
int k;
}CQue;
队列的相关操作函数
void CircularQueueInit(CQue* pq); // 循环队列初始化
bool CircularQueueEmpty(CQue* pq); // 判空
bool CircularQueueFull(CQue* pq); // 判满
bool CircularQueueEnQueue(CQue* pq, QDataType x); // 入数据
bool CircularQueueDeQueue(CQue* pq); // 出数据
QDataType CircularQueueFront(CQue* pq); // 取队头数据
QDataType CircularQueueRear(CQue* pq); // 取队尾数据
void CircularQueueFree(CQue* pq); // 空间释放
其中,入数据和出数据函数都要考虑边界。
对于取队尾数据
QDataType CircularQueueRear(CQue* pq); // 取队尾数据
需要取到pq->rear
的前一个,在这里统一采用**(pq->rear+MAX-1)%mAX
**
相关源码
Queue.h
#pragma once
#include<stdio.h>
#include<stdbool.h>
#include<stdlib.h>
#include<assert.h>
typedef int QDataType;
typedef struct QueueNode
{
QDataType data;
struct QueueNode* next;
}QNode;
typedef struct Queue
{
QNode* head;
QNode* tail;
int size;
}Que;
void QueueInit(Que* pq); // 队列初始化函数
void QueueDestroy(Que* pq); // 队列销毁函数
void QueuePush(Que* pq, QDataType x); // 增加数据
void QueuePop(Que* pq); // 删除数据
QDataType QueueFront(Que* pq); // 取队头数据
QDataType QueueBack(Que* pq); // 取队对尾数据
bool QueueEmpty(Que* pq); // 判断队列是否为空
int QueueSize(Que* pq); // 返回队列数据个数
Queue.c
#include "Queue.h"
void QueueInit(Que* pq) // 队列初始化函数
{
assert(pq != NULL);
pq->head = pq->tail = NULL;
pq->size = 0;
}
void QueueDestroy(Que* pq) // 队列销毁函数
{
assert(pq);
QNode* cur = pq->head;
while (cur != NULL)
{
QNode* next = cur->next;
free(cur);
cur = next;
}
pq->head = pq->tail = NULL;
pq->size = 0;
}
void QueuePush(Que* pq, QDataType x) // 增加数据
{
assert(pq);
QNode* newNode = (QNode*)malloc(sizeof(QNode));
if (newNode == NULL)
{
perror("malloc fail");
exit(-1);
}
newNode->data = x;
newNode->next = NULL;
if (pq->tail == NULL)
{
pq->head = pq->tail = newNode;
}
else
{
pq->tail->next = newNode;
pq->tail = newNode;
}
pq->size++;
}
void QueuePop(Que* pq) // 删除数据
{
assert(pq != NULL);
assert(pq->head != NULL);
if (pq->head == pq->tail) // 剩了一个结点,单独判断
{
free(pq->head);
pq->head = pq->tail = NULL;
}
else
{
QNode* del = pq->head;
pq->head = pq->head->next;
free(del);
}
pq->size--;
}
QDataType QueueFront(Que* pq) // 取队头数据
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->head->data;
}
QDataType QueueBack(Que* pq) // 取队对尾数据
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->tail->data;
}
bool QueueEmpty(Que* pq) // 判断队列是否为空
{
assert(pq);
return pq->head == NULL;
}
int QueueSize(Que* pq) // 返回队列数据个数
{
assert(pq);
return pq->size;
}
CircularQueue.h
#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
#define MAX 5
typedef int QDataType;
typedef struct CircularQueue
{
QDataType* a;
int front;
int rear;
int k;
}CQue;
void CircularQueueInit(CQue* pq); // 循环队列初始化
bool CircularQueueEmpty(CQue* pq); // 判空
bool CircularQueueFull(CQue* pq); // 判满
bool CircularQueueEnQueue(CQue* pq, QDataType x); // 入数据
bool CircularQueueDeQueue(CQue* pq); // 出数据
QDataType CircularQueueFront(CQue* pq); // 取队头数据
QDataType CircularQueueRear(CQue* pq); // 取队尾数据
void CircularQueueFree(CQue* pq); // 空间释放
CircularQueue.c
#include"CircularQueue.h"
void CircularQueueInit(CQue* pq)
{
assert(pq);
// 多开一个空间,方便区分空和满
pq->a = (QDataType*)malloc(sizeof(QDataType)*MAX);
if (pq->a == NULL)
{
perror("malloc fail");
exit(-1);
}
pq->front = pq->rear = 0;
pq->k = MAX - 1;
}
bool CircularQueueEmpty(CQue* pq)
{
assert(pq);
return pq->front == pq->rear;
}
bool CircularQueueFull(CQue* pq)
{
assert(pq);
return (pq->rear + 1) % MAX == pq->front;
}
bool CircularQueueEnQueue(CQue* pq, QDataType x)
{
if (CircularQueueFull(pq))
return false;
pq->a[pq->rear] = x;
pq->rear++;
//if (pq->rear == MAX)
// pq->rear = 0;
pq->rear %= MAX;
return true;
}
bool CircularQueueDeQueue(CQue* pq)
{
assert(pq);
if (CircularQueueEmpty(pq))
return false;
pq->front++;
pq->front %= MAX;
return true;
}
QDataType CircularQueueFront(CQue* pq)
{
assert(pq);
assert(!CircularQueueEmpty(pq));
return pq->a[pq->front];
}
QDataType CircularQueueRear(CQue* pq)
{
assert(pq);
assert(!CircularQueueEmpty(pq));
return pq->a[(pq->rear + pq->k) % MAX];
}
void CircularQueueFree(CQue* pq)
{
assert(pq);
free(pq->a);
pq->a = NULL;
}