什么是队列?
只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出
FIFO(First In First Out) 入队列:进行插入操作的一端称为队尾 出队列:进行删除操作的一端称为队头
从图可知,队列其实可以用数组实现,也可以用链表实现,但是如果用数组,在队头删除数据的时候,会比较麻烦,所以此文章是以链表为基础进行队列的实现。
队列的的结构体定义和函数声明
typedef int QDataType;
typedef struct QueueNode //整体框架,一个元素的结构
{
struct QueueNode* next;//结构体指针内又存放着其他数据,为单链表
QDataType data;
}QNode;
//内部结构
typedef struct Queue//队列更适合用链式结构去实现,队列的结构
{
QNode* phead;//出数据 用QNode修饰,那么说明phead也可以指向下一个元素next和储存data
QNode* ptail;//入数据
int size;//记录大小,虽然链表没有大小限制,但是可以方便读取大小,不需要遍历
}Queue;
//为什么不用二级指针——指针放在结构体内,改变结构体,指针是结构体的成员,并不是结构体指针
void QueueInit(Queue* pq);
void QueueDestroy(Queue* pq);
void QueuePush(Queue* pq, QDataType x);
void QueuePop(Queue* pq);//头删
bool QueueEmpty(Queue* pq);
QDataType QueueFront(Queue* pq);//取头部
QDataType QueueBack(Queue* pq);//取队尾
int QueueSize(Queue* pq);
需要注意的是
1.队列是一个整体,但是内部又是一个个由指针定义起来的节点。所以我们需要定义两个结构体,一个是队列的结构,一个是元素节点的结构。
2.队列尽管是一个整体,但是其内部的头尾指针终究还是节点,所以内部的指针还是用节点的结构体指针来定义。
队列元素的插入
我们知道是“FIFO"结构,所以要是插入数据,那么就只能在队列的尾部进行插入。
void QueuePush(Queue* pq, QDataType x)
{
assert(pq);//整体断言
QNode* newnode = (QNode*)malloc(sizeof(QNode));//建立一个空的
if (newnode == NULL)
{
perror("malloc fail\n");
return;
}
newnode->data = x; // 如果新建的是空,说明是第一个元素
newnode->next = NULL;
if (pq->ptail == NULL)//第一个元素
{
assert(pq->phead==NULL);//头尾部断言
pq->phead = pq->ptail = newnode;
}
else
{
pq->ptail->next = newnode;
pq->ptail = newnode;//成为新的尾部
}
pq->size++;
}
代码中有两个结构体指针变量,一个是“Queue*” ;另一个是"QNode*"
我们对于结构体,改变的是整个结构体,所以传入的应该是结构体,用“Queue*"来修饰
但是建立节点,就要用节点的结构体来定义“QNode*”来修饰。
队列元素的删除
void QueuePop(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
//1.一个节点,直接释放
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--;
}
取队头和队尾元素
此处本来不能取尾的,但还是给出来代码吧。
QDataType QueueFront(Queue* pq)//取头部
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->phead->data;
}
QDataType QueueBack(Queue* pq)//取队尾
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->ptail->data;
}
队列的元素个数
int QueueSize(Queue* pq)
{
assert(pq);
return pq->size;//返回元素个数
}
队列的初始化
void QueueInit(Queue* pq)
{
assert(pq);
pq->phead = NULL;
pq->ptail = NULL;
pq->size = 0;
}
队列的删除
void QueueDestroy(Queue* pq)
{
assert(pq);
//第一个元素就是Queue中的phead,同时也是QNode的初始指针位置
QNode* cur = pq->phead;//用结构体指针定义的cur遍历指针,故直接作用于phead
while (cur)//处理中间的指针,中间释放内存,头尾要置空
{
QNode* next = cur->next;
free(cur);
cur=next;
}
pq->phead = pq->ptail = NULL;//头尾也要变为NULL
pq->size = 0;
}
队列的删除也就是一个个指针节点的删除,释放内存后即可。
判断队列是否为空
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->size == 0;//为空,那么元素数为0,否则不为0
}
队列的实现的底层是链表,难点在于两个结构体的构建,一个是节点的结构体构建,而且节点要有next指针指向下一个元素,队列结构的结构体,需要有一个头指针和尾指针,而且这两个指针也是要由节点的结构体来构建的。