制作不易,三连支持一下呗!!!
文章目录
前言
上一篇博客我们介绍了栈的结构,这篇博客我们将介绍一个与栈类似的数据结构------队列
一、队列是什么?
在日常生活中我们都排过队,数据结构中的队列顾名思义其实就是类似我们生活中的队伍,先到先得。因此,队列是一种先进先出的数据结构。
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出(FIFO)的特性
队尾:进行插入操作的一端称为队尾
队头:进行删除操作的一端称为队头
二、队列的实现
1.队列的结构
队列也可以用数组或链表的结构实现,使用链表的结构要更优一些,因为如果使用数组的结构,出队列在数组头上出数据,要挪动所以数据,效率较低一些。
又因为队列插入是尾插,删除是头删,为了方便我们进行删除操作,我们再用一个指针指向队尾,这样我们就不需要循环遍历来找尾节点,提高效率。
为了方便我们实现求队列长度的接口,我们还定义了一个size变量来记录队列的长度。
因此队列结构如下所示:
cpp
typedef int QDateType;
typedef struct QueueNode
{
QDateType val;
struct QueueNode* next;
}QNode;
typedef struct Queue
{
QNode* phead;
QNode* ptail;
int size;
}Queue;
2.队列的初始化和销毁
初始化:
cpp
void QueueInit(Queue* pq)
{
assert(pq);
pq->phead = pq->ptail = NULL;
pq->size = 0;
}
初始化很简单,这里不再详细介绍。
销毁:
类似链表的销毁,我们需要利用循环的方式,一个节点一个节点的释放,最后将头指针和尾指针置为NULL。
cpp
void QueueDestroy(Queue* pq)
{
assert(pq);
QNode* cur = pq->phead;
while (cur)
{
QNode* next = cur->next;
free(cur);
cur = next;
}
pq->phead = pq->ptail = NULL;
pq->size = 0;
}
3.队列的插入和删除
1.插入:
与链表相似,,由于我们没有带哨兵卫,插入时就需要判断队列是否为空,如果为空就将头尾指针都指向第一个节点,不为空就在尾节点后面尾插即可。
代码:
cpp
void QueuePush(Queue* pq, QDateType x)
{
assert(pq);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
perror("malloc:");
return;
}
newnode->val = x;
newnode->next = NULL;
if (pq->ptail)
{
pq->ptail->next = newnode;
pq->ptail = newnode;
}
else
{
pq->phead = pq->ptail = newnode;
}
pq->size++;
}
2.删除:
删除要分为三种情况:
①多个节点时,直接将头节点释放,并将头节点更新。
②只有一个节点时,释放头节点,并要将头尾指针都置为NULL。
③队列为空时,不能进行删除操作!!!
代码:
cpp
void QueuePop(Queue* pq)
{
assert(pq);
assert(pq->ptail);
//一个节点
if (pq->phead->next == NULL)
{
free(pq->phead);
pq->phead = pq->ptail = NULL;
}
//多个节点
else
{
QNode* next = pq->phead->next;
free(pq->phead);
pq->phead = next;
}
pq->size--;
}
4. 其他一些接口
①取队头元素
cpp
QDateType QueueFront(Queue* pq)
{
assert(pq);
//队列不能为空
assert(pq->ptail);
return pq->phead->val;
}
②取队尾元素
cpp
QDateType QueueBack(Queue* pq)
{
assert(pq);
//队列不能为空
assert(pq->ptail);
return pq->ptail->val;
}
③判断队列是否为空
cpp
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->size == 0;
}
注:这里有多种写法,可以通过size来判断,也可以通过头尾指针是否为空指针来判断
④求队列长度
cpp
int QueueSize(Queue* pq)
{
assert(pq);
return pq->size;
}
总结
队列的难度不是很大,与单链表有许多相似之处,细节可以去看看链表部分的博客!!!
全部代码如下:
cpp
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int QDateType;
typedef struct QueueNode
{
QDateType val;
struct QueueNode* next;
}QNode;
typedef struct Queue
{
QNode* phead;
QNode* ptail;
int size;
}Queue;
void QueueInit(Queue* pq);
void QueueDestroy(Queue* pq);
void QueuePush(Queue* pq, QDateType x);
void QueuePop(Queue* pq);
QDateType QueueFront(Queue* pq);
QDateType QueueBack(Queue* pq);
bool QueueEmpty(Queue* pq);
int QueueSize(Queue* pq);
void QueueInit(Queue* pq)
{
assert(pq);
pq->phead = pq->ptail = NULL;
pq->size = 0;
}
void QueueDestroy(Queue* pq)
{
assert(pq);
QNode* cur = pq->phead;
while (cur)
{
QNode* next = cur->next;
free(cur);
cur = next;
}
pq->phead = pq->ptail = NULL;
pq->size = 0;
}
void QueuePush(Queue* pq, QDateType x)
{
assert(pq);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
perror("malloc:");
return;
}
newnode->val = x;
newnode->next = NULL;
if (pq->ptail)
{
pq->ptail->next = newnode;
pq->ptail = newnode;
}
else
{
pq->phead = pq->ptail = newnode;
}
pq->size++;
}
void QueuePop(Queue* pq)
{
assert(pq);
assert(pq->ptail);
//一个节点
if (pq->phead->next == NULL)
{
free(pq->phead);
pq->phead = pq->ptail = NULL;
}
//多个节点
else
{
QNode* next = pq->phead->next;
free(pq->phead);
pq->phead = next;
}
pq->size--;
}
QDateType QueueFront(Queue* pq)
{
assert(pq);
//队列不能为空
assert(pq->ptail);
return pq->phead->val;
}
QDateType QueueBack(Queue* pq)
{
assert(pq);
//队列不能为空
assert(pq->ptail);
return pq->ptail->val;
}
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->size == 0;
}
int QueueSize(Queue* pq)
{
assert(pq);
return pq->size;
}