上一章我们对一种特殊的线性表---栈做了代码实现,这一章我们来对另一种特殊的线性表---队列的简单功能进行代码实现。
文章目录
- 前言
- 队列的初始化
- 队列的销毁
- 入队列
- 出队列
- 查看队首数据
- 查看队尾数据
- 查看队列长度
- 查看队列是否为空
- 头文件代码
- 源文件代码
前言
typedef struct queueNode {
struct queueNode* next;
dataType val;
}queueNode;
typedef struct queue {
queueNode* head;
queueNode* tail;
}queue;
队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。
上一章我们实现栈用了顺序存储的方式,但实现队列时如果用顺序存储就会很麻烦,因为队列是先进先出的,所以如果顺序存储的话每次出数据我们都需要挪动剩下所有的数据,而链式存储的话我们可以设置一个head结点一个tail结点,这样尾插和头删都很方便,适合用来进行队列的实现。同样我们结构体定义一个queueNode来表示队列内每个数据和存储的下个数据的地址,再定义一个queue用来存储队列队首和队尾的地址。
队列的初始化
void queueInit(queue* pq) {
assert(pq);
pq->head = NULL;
pq->tail = NULL;
}
初始化头尾都是NULL,不再赘述。
队列的销毁
void queueDestroy(queue* pq) {
assert(pq);
while (pq->head) {
queueNode* headNext = pq->head->next;
free(pq->head);
pq->head = headNext;
}
pq->head = pq->tail = NULL;
}
队列销毁时我们要注意可以先定义一个临时变量来存头的下一个位置,不然free掉头结点之后我们就找不到下一个位置了,当头结点指向空时说明已经清空队列了,最后把头尾都置空即可。
入队列
void queuePush(queue* pq, dataType val) {
assert(pq);
queueNode* newnode = (queueNode*)malloc(sizeof(queueNode));
newnode->next = NULL;
newnode->val = val;
if (pq->head) {
pq->head = pq->tail = newnode;
}
else {
pq->tail->next = newnode;
pq->tail = newnode;
}
}
因为我们要实现动态存储,所以先malloc出一个队列元素,这里同样需要注意如果是空队列,我们只需要把新元素赋值给头尾即可,如果不为空,那么原来的队尾指向该新元素,再让新元素当尾即可。
出队列
void queuePop(queue* pq) {
assert(pq && pq->head);
queueNode* headNext = pq->head->next;
free(pq->head);
pq->head = headNext;
if (pq->head == NULL) {
pq->tail = NULL;//此时head为空,tail指向的那块空间已经free,此时如果不置空则tail是野指针
}
}
这里需要注意的就是队列不能为空,还有一点就是当出队列出到空时head指向空,此时tail还指向队尾,但此时这块空间已经被free掉了,tail就是一个野指针了,所以也要置空。
查看队首数据
dataType queueFront(queue* pq) {
assert(pq && pq->head);
return pq->head->val;
}
查看队尾数据
dataType queueBack(queue* pq) {
assert(pq && pq->head);
return pq->tail->val;
}
查看队列长度
size_t queueSize(queue* pq) {
assert(pq);
size_t size = 0;
queueNode* cur = pq->head;
while (cur) {
size++;
cur = cur->next;
}
return size;
}
我们还可以一开始就定义一个变量来记录队列长度,每入队列一次就++,最后直接返回那个值,时间复杂度时O(1),如果不定义就用临时变量从头遍历到尾每次++,时间复杂度是O(N)。
查看队列是否为空
bool checkQueueEmpty(queue* pq) {
assert(pq);
return pq->head == NULL;
}
头文件代码
#pragma once
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>
typedef int dataType;
typedef struct queueNode {
struct queueNode* next;
dataType val;
}queueNode;
typedef struct queue {
queueNode* head;
queueNode* tail;
}queue;
//队列的初始化
void queueInit(queue* pq);
//队列的销毁
void queueDestroy(queue* pq);
//入队列
void queuePush(queue* pq, dataType val);
//出队列
void queuePop(queue* pq);
//查看队首数据
dataType queueFront(queue* pq);
//查看队尾数据
dataType queueBack(queue* pq);
//查看队列长度
size_t queueSize(queue* pq);
//查看队列是否为空
bool checkQueueEmpty(queue* pq);
源文件代码
#include "queue.h"
void queueInit(queue* pq) {
assert(pq);
pq->head = NULL;
pq->tail = NULL;
}
void queueDestroy(queue* pq) {
assert(pq);
while (pq->head) {
queueNode* headNext = pq->head->next;
free(pq->head);
pq->head = headNext;
}
pq->head = pq->tail = NULL;
}
void queuePush(queue* pq, dataType val) {
assert(pq);
queueNode* newnode = (queueNode*)malloc(sizeof(queueNode));
newnode->next = NULL;
newnode->val = val;
if (pq->head) {
pq->head = pq->tail = newnode;
}
else {
pq->tail->next = newnode;
pq->tail = newnode;
}
}
void queuePop(queue* pq) {
assert(pq && pq->head);
queueNode* headNext = pq->head->next;
free(pq->head);
pq->head = headNext;
if (pq->head) {
pq->tail = NULL;//此时head为空,tail指向的那块空间已经free,此时如果不置空则tail是野指针
}
}
dataType queueFront(queue* pq) {
assert(pq && pq->head);
return pq->head->val;
}
dataType queueBack(queue* pq) {
assert(pq && pq->head);
return pq->tail->val;
}
size_t queueSize(queue* pq) {
assert(pq);
size_t size = 0;
queueNode* cur = pq->head;
while (cur) {
size++;
cur = cur->next;
}
return size;
}
bool checkQueueEmpty(queue* pq) {
assert(pq);
return pq->head == NULL;
}