当前位置: 首页 > news >正文

【数据结构】队列详解

一、队列(Queue)

01 队列的概念

概念:

① 队列只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表。

② 入队列,进行插入操作的一端称为 队尾。出队列,进行删除操作的一端称为 队头

③ 队列中的元素遵循先进先出的原则,即 FIFO 原则(First In First Out)。

02 队列的结构

结构:

二、队列的定义

01 链式队列

typedef int QueueDataType;typedef struct QueueNode {struct QueueNode* next;QueueDataType data;
} QueueNode;typedef struct Queue {QueueNode* pHead;QueueNode* pTail;
} Queue;

02 接口函数

这是需要实现几个接口函数:

// 初始化队列 
void QueueInit(Queue* q);
// 队尾入队列 
void QueuePush(Queue* q, QueueDataType data);
// 队头出队列 
void QueuePop(Queue* q);
// 获取队列头部元素 
QueueDataType QueueFront(Queue* q);
// 获取队列队尾元素 
QueueDataType QueueBack(Queue* q);
// 获取队列中有效元素个数 
int QueueSize(Queue* q);
// 检测队列是否为空
bool QueueEmpty(Queue* q);
// 销毁队列 
void QueueDestroy(Queue* q);

三、队列的实现

01 初始化(QueueInit)

Queue.h:

// 初始化队列 
void QueueInit(Queue* q);

Queue.c:

/* 初始化队列 */
void QueueInit(Queue* q) {assert(q);q->pHead = q->pTail = NULL;
}

解析:首先使用断言防止传入的 q 为空。初始化只需要把头指针和尾指针都置成空即可。

02 销毁(QueueDestory)

Queue.h:

// 销毁队列
void QueueDestroy(Queue* q);

Queue.c:

/* 销毁队列 */
void QueueDestroy(Queue* q) {assert(q);QueueNode* cur = q->pHead;while (cur) {QueueNode* curNext = cur->next;free(cur);cur = curNext;}q->pHead = q->pTail = NULL;
}

解读:

① 首先断言防止传入的 q 为空。

② 销毁要把所有节点都释放掉,我们创建遍历指针 cur 遍历整个队列。既然要释放 cur 指向的节点,为了防止释放 cur 之后找不到其下一个节点导致无法移动,我们这里创建一个类似于信标性质的指针 curNext 来记录一下 cur 的下一个节点,之后再 free cur,这样就可以移动 cur 了。

③ 最后为了防止野指针,还需要把头指针和尾指针都置为空。

03 判断队列是否为空(QueueEmpty)

Queue.h:

// 检测队列是否为空
bool QueueEmpty(Queue* q);

Queue.c:

/* 检测队列是否为空 */
bool QueueEmpty(Queue* q) {assert(q);return q->pHead == NULL;
}

04 入队(QueuePush)

Queue.h:

// 队尾入队列 
void QueuePush(Queue* q, QueueDataType data);

Queue.c:

/* 队尾入队列 */
void QueuePush(Queue* q, QueueDataType data) {assert(q);// 创建新结点QueueNode* new_node = (QueueNode*)malloc(sizeof(QueueNode));if (new_node == NULL) {printf("malloc failed");exit(-1);}new_node->data = data;new_node->next = NULL;/* 情况1:队列为空:既当头又当尾* [new_node]* ↑       ↑* pHead pTail*情况2:队列不为空:队尾入数据* [] ->[] ->[] ->[]   ->[new_node]* pHead         pTail     pTail->next*/if (q->pHead == NULL) {q->pHead = q->pTail = new_node;} else {q->pTail->next = new_node;q->pTail = new_node;}
}

解读:

① 首先断言防止传入的pQ为空。

② 我们首先要创建新节点。通过 malloc 动态内存开辟一块 QueueNode 大小的空间。最后放置数据,将待插入的数据 data 交给 datanext 默认置空,和之前学链表一样,这里就不过多赘述了。

③ 新节点创建好后,我们可以开始写入队的操作了。首先要理解队列的性质:队尾入数据,队头出数据。这里既然是入队,就要在队尾进行插入。这里我们还要考虑到如果队列为空的情况,这时我们要把头指针和尾指针都交付给 new_node

当队列为空时,令头指针和尾指针都指向 new_node ,当队列不为空时,在尾部的下一个节点放置 new_node ,随后再更新尾指针让其指向新的队尾。

05 出队(QueuePop)

Queue.h:

// 队头出队列 
void QueuePop(Queue* q);

Queue.c:

/* 队头出队列 */
void QueuePop(Queue* q) {assert(q && !QueueEmpty(q));QueueNode* headNext = q->pHead->next;free(q->pHead);q->pHead = headNext;if (q->pHead == NULL)q->pTail = NULL;
}

解读:

① 首先断言防止传入的 q 为空,这里还要防止队列为空,如果队列为空还要求出队的话会出问题,所以这里要断言一下 QueueEmpty 为假。

② 思路草图如下:

出数据需要释放,和销毁一样,这里使用一个类似于信标性质的指针来记录 pHead 的下一个节点,之后我们就可以大胆地释放 pHead 了。free 掉之后更新头即可,令头指针指向 headNext 即可。 

注意:这里还要考虑一个问题,如果队内都被删完了,pHead 往后走指向空,但是 pTail 仍然指向那块已经被 free 掉的空间。pTail 就是一个典型的野指针。我们可以不用担心 pHead,因为后面没有数据他会自然指向 NULL,但是我们这里得关注 pTail ,手动处理一下它。如果 pHead 为空,我们就把 pTail 也置为空即可。

06 返回队头数据(QueueFront)

Queue.h:

// 获取队列头部元素 
QueueDataType QueueFront(Queue* q);

Queue.c:

/* 获取队列头部元素 */
QueueDataType QueueFront(Queue* q) {assert(q && !QueueEmpty(q));return q->pHead->data;
}

07 返回队尾数据(QueueBack)

Queue.h:

// 获取队列队尾元素 
QueueDataType QueueBack(Queue* q);

Queue.c:

/* 获取队列队尾元素 */
QueueDataType QueueBack(Queue* q) {assert(q && !QueueEmpty(q));return q->pTail->data;
}

08 获取队列中有效元素个数(QueueSize)

Queue.h:

// 获取队列中有效元素个数 
int QueueSize(Queue* q);

Queue.c:

/* 获取队列中有效元素个数 */
int QueueSize(Queue* q) {assert(q);int cnt = 0;QueueNode* cur = q->pHead;while (cur) {++cnt;cur = cur->next;}return cnt;
}

解读:这里我们采用计数器法来求大小即可,调用一次就是 O(N) ,也没什么不好的。

① 首先断言防止传入的 q 为空。

② 创建计数器变量和遍历指针 cur,遍历整个队列并计数,最后返回计数的结果即可。

09 完整代码

Queue.h:

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>typedef int QueueDataType;typedef struct QueueNode {struct QueueNode* next;QueueDataType data;
} QueueNode;typedef struct Queue {QueueNode* pHead;QueueNode* pTail;
} Queue;// 初始化队列 
void QueueInit(Queue* q);
// 队尾入队列 
void QueuePush(Queue* q, QueueDataType data);
// 队头出队列 
void QueuePop(Queue* q);
// 获取队列头部元素 
QueueDataType QueueFront(Queue* q);
// 获取队列队尾元素 
QueueDataType QueueBack(Queue* q);
// 获取队列中有效元素个数 
int QueueSize(Queue* q);
// 检测队列是否为空
bool QueueEmpty(Queue* q);
// 销毁队列 
void QueueDestroy(Queue* q);

Queue.c:

#include "Queue.h"/* 初始化队列 */
void QueueInit(Queue* q) {assert(q);q->pHead = q->pTail = NULL;
}/* 队尾入队列 */
void QueuePush(Queue* q, QueueDataType data) {assert(q);// 创建新结点QueueNode* new_node = (QueueNode*)malloc(sizeof(QueueNode));if (new_node == NULL) {printf("malloc failed");exit(-1);}new_node->data = data;new_node->next = NULL;/* 情况1:队列为空:既当头又当尾* [new_node]* ↑       ↑* pHead pTail*情况2:队列不为空:队尾入数据* [] ->[] ->[] ->[]   ->[new_node]* pHead         pTail     pTail->next*/if (q->pHead == NULL) {q->pHead = q->pTail = new_node;} else {q->pTail->next = new_node;q->pTail = new_node;}
}/* 队头出队列 */
void QueuePop(Queue* q) {assert(q && !QueueEmpty(q));QueueNode* headNext = q->pHead->next;free(q->pHead);q->pHead = headNext;if (q->pHead == NULL)q->pTail = NULL;
}/* 获取队列头部元素 */
QueueDataType QueueFront(Queue* q) {assert(q && !QueueEmpty(q));return q->pHead->data;
}/* 获取队列队尾元素 */
QueueDataType QueueBack(Queue* q) {assert(q && !QueueEmpty(q));return q->pTail->data;
}/* 获取队列中有效元素个数 */
int QueueSize(Queue* q) {assert(q);int cnt = 0;QueueNode* cur = q->pHead;while (cur) {++cnt;cur = cur->next;}return cnt;
}/* 检测队列是否为空 */
bool QueueEmpty(Queue* q) {assert(q);return q->pHead == NULL;
}/* 销毁队列 */
void QueueDestroy(Queue* q) {assert(q);QueueNode* cur = q->pHead;while (cur) {QueueNode* curNext = cur->next;free(cur);cur = curNext;}q->pHead = q->pTail = NULL;
}

文章转载自:

http://qskXJUQQ.skrcn.cn
http://0d6F9hkB.skrcn.cn
http://Ezb8kQiz.skrcn.cn
http://snwkbzII.skrcn.cn
http://dKno3qVj.skrcn.cn
http://FOTiMEOP.skrcn.cn
http://UXA4sM8B.skrcn.cn
http://mLI95fGZ.skrcn.cn
http://dSQdGCr2.skrcn.cn
http://AzZeMmVy.skrcn.cn
http://TF17Ry5j.skrcn.cn
http://ZylA4ZX0.skrcn.cn
http://HRqTCB1L.skrcn.cn
http://k65ZwKD3.skrcn.cn
http://k1f8as9X.skrcn.cn
http://2D4IcPF7.skrcn.cn
http://U4VBMls2.skrcn.cn
http://Cu7ifXoV.skrcn.cn
http://1mueE6CR.skrcn.cn
http://SU9RE987.skrcn.cn
http://MgeaO9LF.skrcn.cn
http://ofUw7D01.skrcn.cn
http://RNrQOdAS.skrcn.cn
http://bM9Uu8AJ.skrcn.cn
http://P9L7CSxL.skrcn.cn
http://BrnfYFeS.skrcn.cn
http://gOJvUzeJ.skrcn.cn
http://hmaEd26J.skrcn.cn
http://fmgPSTzL.skrcn.cn
http://3VDEJcho.skrcn.cn
http://www.dtcms.com/a/378500.html

相关文章:

  • C++/QT
  • GPT 系列论文1-2 两阶段半监督 + zero-shot prompt
  • 昆山精密机械公司8个Solidworks共用一台服务器
  • MasterGo钢笔Pen
  • 【算法--链表】143.重排链表--通俗讲解
  • 数据库的回表
  • 《Learning Langchain》阅读笔记13-Agent(1):Agent Architecture
  • MySQL索引(二):覆盖索引、最左前缀原则与索引下推详解
  • 【WS63】星闪开发资源整理
  • 守住矿山 “生命线”!QB800系列在线绝缘监测在矿用提升机电传系统应用方案
  • Altium Designer(AD)原理图更新PCB后所有器件变绿解决方案
  • DIFY 项目中通过 Makefile 调用 Dockerfile 并使用 sudo make build-web 命令构建 web 镜像的方法和注意事项
  • 联合索引最左前缀原则原理索引下推
  • 平衡车 -- 速度环
  • BPE算法深度解析:从零到一构建语言模型的词元化引擎
  • DIPMARK:一种隐蔽、高效且具备鲁棒性的大语言模型水印技术
  • mysql多表联查
  • 审美积累 | 移动端仪表盘
  • 面阵结构光3D相机三维坐标计算
  • 【大前端++】几大特征
  • 【持续更新】高质量的项目开发过程(C++)(前后端)
  • 淘宝商品视频批量自动化获取的常见渠道分享
  • ABAP 将多层json逐层解析转成内表
  • 一样的糖果
  • linux x86_64中打包qt
  • Windows 10 22H2 64位 【原版+优化版、版本号:19045.6332】
  • 学习日记-CSS-day53-9.11
  • 线程的创建.销毁
  • pg卡死处理
  • 装饰器模式在Spring中的案例