📖 前言:在我们使用电脑的时候,我们有时会出现疑似死机的状态,鼠标点什么似乎都没有用。当你失去耐心准备重启的时候,它突然像酒醒了一样,把你刚才单击的所有操作全部都按顺序执行了一遍。这是因为操作系统在当时可能CPU一时忙不过来,等前面的事忙完后后面的多个指令需要通过一个通道输出,按先后次序排队执行造成的结果。
再比如我们在打客服人工电话的时候,常常会被提示要求等待,并会告诉你前面还有多少人,这就是对所有当前拨打客服电话的客户进行了排队处理。
操作系统和客服系统中都是应用的一种数据结构来实现刚才提到的先进先出的排序功能,这就是队列。
🎓 作者:HinsCoder
📦 作者的GitHub:代码仓库
📌 往期文章&专栏推荐:
目录📌
🕒 1. 什么是队列
- 栈是一种特殊的
线性表
- 只允许在一端进行
插入
操作,在另一端进行删除
操作 - 队列中的数据遵守FIFO(First In First Out)【即元素的先进先出】的原则
- 进行数据插入的一端称为队头,数据删除的一端成为队尾。
🕒 2. 队列的实现
我们采用单链表的结构实现,因为用数组效率较低。
接下来我们开始实现队列的接口
🕘 2.1 定义链式队列
typedef int QDataType;
typedef struct QueueNode
{
struct QueueNode* next;
QDataType data;
}QNode;
typedef struct Queue
{
QNode* head; //定义一个头指针,方便头删
QNode* tail; //定义一个尾指针,方便尾插
}Queue;
⚡ 注意:
-
因为
队列
不像单链表
需要遍历每个结点去访问每个结点的数据,所以我们只需要获取队头和队尾的数据即可 -
这也是为什么设置两个指针:
头指针
、尾指针
🕘 2.2 初始化队列
void QueueInit(Queue* pq)
{
assert(pq);
pq->head = pq->tail = NULL;
pq->size = 0;
}
🕘 2.3 销毁队列
void QueueDestroy(Queue* pq)
{
assert(pq);
QNode* cur = pq->head;
while (cur)
{
QNode* del = cur;
cur = cur->next;
free(del);
}
pq->head = pq->tail = NULL;
}
🕘 2.4 队列的判空
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->head == NULL && pq->tail == NULL;
}
🕘 2.5 入队(尾插)
void QueuePush(Queue* pq, QDataType x)
{
assert(pq);
//创建新结点
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
perror("malloc fail");
exit(-1);
}
else
{
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++;
}
🕘 2.6 出队(头删)
void QueuePop(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
if (pq->head->next == NULL) //只有一个结点的时候
{
free(pq->head);
pq->head = pq->tail = NULL;
}
else
{
QNode* del = pq->head;
pq->head = pq->head->next;
free(del);
del = NULL;
}
pq->size--;
}
🕘 2.7 获取元素数量
💡 思路1:(时间复杂度为O(n))
QNode* cur = pq->head;
int n = 0;
while (cur)
{
n++;
cur = cur->next;
}
return n;
💡 思路:(时间复杂度为O(1))
定义改一改,多一个size,在随后的入队操作size++
typedef struct Queue
{
QNode* head; //定义一个头指针,方便头删
QNode* tail; //定义一个尾指针,方便尾插
int size;
}Queue;
int QueueSize(Queue* pq)
{
assert(pq);
return pq->size;
}
🕘 2.8 获取队头数据
QDataType QueueFront(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->head->data;
}
🕘 2.9 获取队尾数据
QDataType QueueBack(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->tail->data;
}
⌛ 总结:本期关于队列的实现其实没有什么新的内容,如果有哪里不清楚的都属于单链表相关知识没掌握好,移步 🔎 【数据结构详解】——线性表之单链表(动图详解)哟~~
🕒 3. 完整源码
// Queue.h
#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
typedef int QDataType;
typedef struct QueueNode
{
struct QueueNode* next;
QDataType data;
}QNode;
typedef struct Queue
{
QNode* head; //定义一个头指针,方便头删
QNode* tail; //定义一个尾指针,方便尾插
int size;
}Queue;
void QueueInit(Queue* pq);
//void QueueInit(QNode** pq); //二级指针传参方式
void QueueDestroy(Queue* pq);
void QueuePush(Queue* pq, QDataType x);
void QueuePop(Queue* pq);
QDataType QueueFront(Queue* pq);
QDataType QueueBack(Queue* pq);
bool QueueEmpty(Queue* pq);
int QueueSize(Queue* pq);
// Queue.c
#include"Queue.h"
void QueueInit(Queue* pq)
{
assert(pq);
pq->head = pq->tail = NULL;
pq->size = 0;
}
void QueueDestroy(Queue* pq)
{
assert(pq);
QNode* cur = pq->head;
while (cur)
{
QNode* del = cur;
cur = cur->next;
free(del);
}
pq->head = pq->tail = NULL;
}
void QueuePush(Queue* pq, QDataType x)
{
assert(pq);
//创建新结点
QNode* newnode = (QNode*)malloc(sizeof(QNode));
if (newnode == NULL)
{
perror("malloc fail");
exit(-1);
}
else
{
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(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
if (pq->head->next == NULL) //只有一个结点的时候
{
free(pq->head);
pq->head = pq->tail = NULL;
}
else
{
QNode* del = pq->head;
pq->head = pq->head->next;
free(del);
del = NULL;
}
pq->size--;
}
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;
}
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->head == NULL && pq->tail == NULL;
}
int QueueSize(Queue* pq)
{
assert(pq);
/*QNode* cur = pq->head;
int n = 0;
while (cur)
{
n++;
cur = cur->next;
}
return n;*/
return pq->size;
}
// test.c
#include"Queue.h"
void TestQueue()
{
Queue q;
QueueInit(&q);
QueuePush(&q, 1);
QueuePush(&q, 2);
QueuePush(&q, 3);
printf("%d ", QueueFront(&q));
QueuePop(&q);
printf("%d ", QueueFront(&q));
QueuePop(&q);
QueuePush(&q, 4);
QueuePush(&q, 4);
QueuePush(&q, 4);
while (!QueueEmpty(&q))
{
printf("%d ", QueueFront(&q));
QueuePop(&q);
}
printf("\n");
QueueDestroy(&q);
}
int main()
{
TestQueue();
return 0;
}
OK,以上就是本期知识点“链式队列”的知识啦~~ ,感谢友友们的阅读。后续还会继续更新,欢迎持续关注哟📌~
🎉如果觉得收获满满,可以点点赞👍支持一下哟~