队列的概念及结构
队列: 只允许一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出的特点。
入队列: 进行插入操作的一端是队尾;出队列: 进行删除操作的一端是队头;
为什么选择链表队列
和栈一样,队列可以用数组和链表的方式实现,而队列用链表结构更优。因为因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。
具体代码实现
头文件
头文件进行相关头文件的引用和函数声明。
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
typedef int QDataType;
//链式队列
//先进先出
//单个节点包含一个next指针和数据元素data
typedef struct QueueNode {
struct Queue* next;
QDataType data;
}QueueNode;
typedef struct Queue {
QueueNode* head;
QueueNode* tail;
}Queue;
//如果不定义Queue结构体,需使用二级指针采用下面的格式
//void QueueInit(QueueNode** pphead, QueueNode** pptail);
//初始化
void QueueInit(Queue* pq);
//销毁
void QueueDestory(Queue* pq);
//插入(尾)
void QueuePush(Queue* pq, QDataType x);
//删除(头)
void QueuePop(Queue* pq);
//返回队列顶值
QDataType QueueFront(Queue* pq);
//返回队列尾值
QDataType QueueBack(Queue* pq);
//队列大小(元素个数)
int QueueSize(Queue* pq);
//判断是否为空
bool QueueEmpty(Queue* pq);
由于是链式队列,我们定义一个结构体(节点)包含一个指向后一位的next指针和元素数据data
再定义一个结构体存放指向队列头尾节点的指针(head和tail)
初始化
- 初始化将头尾指针置空
void QueueInit(Queue* pq)
{
assert(pq);
pq->head = NULL;
pq->tail = NULL;
}
销毁队列
从队列顶部开始,逐步释放每个节点,最后将队列的头尾指针置空
void QueueDestory(Queue* pq)
{
assert(pq);
//从队列顶部开始
QueueNode* cur = pq->head;
while (cur != NULL)
{
//逐步释放完队列
QueueNode* next = cur->next;
free(cur);
//到下一位
cur = next;
}
pq->head = pq->tail = NULL;
}
插入元素(尾插)
- 因为是链式队列,插入操作先创建节点并插入元素,判断队列此前是否有元素
- 队列为了实现先进先出,插入/删除元素时应为尾插头删
- 若此前队列没有元素,将
head和tail
都指向newnode - 若此前队列已有元素,将之前的
tail->next
指向newnode,再将newnode变成尾元素
void QueuePush(Queue* pq, QDataType x)
{
assert(pq);
//创建新节点并插入元素
QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
if (newnode == NULL)//是否malloc成功
{
perror("malloc");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
//当此时队列没有元素,将头尾都指向新节点
if (pq->head == NULL)
{
pq->head = pq->tail = newnode;
}
else
//队列有元素,将之前的尾节点的next给newnode,将newnode改为尾节点(tail指向newnode)
{
pq->tail->next = newnode;
pq->tail = newnode;
}
}
删除元素(头删)
- 先判断队列是否为空
- 找到第二个节点元素,释放第一个节点将
head
指向第二个节点 - 如果删除完最后一个元素,此时head为空,将tail置空(防止野指针)
void QueuePop(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));//不为空
QueueNode* next = pq->head->next;
free(pq->head);
pq->head = next;
//节点被删完
if (pq->head == NULL)
pq->tail = NULL;//防止tail野指针
}
返回队头
- 断言队列不能为空
- 返回head指针处的data
QDataType QueueFront(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->head->data;
}
返回队尾
- 断言队列不为空
- 返回队尾的数据
QDataType QueueBack(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->tail->data;
}
计算队列大小(元素个数)
- 断言队列不为空
- 从头向后走节点,直到走到空,每次++size
int QueueSize(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
int size = 0;
QueueNode* cur = pq->head;
while (cur)
{
cur = cur->next;
++size;
}
return size;
}
判断队列是否为空
- 当head为空时队列为空(无需判断tail)
- 由于是布尔类型,直接返回表达式即可
bool QueueEmpty(Queue* pq)
{
assert(pq);
//return pq->head == NULL && pq->tail == NULL;
return pq->head == NULL;
}