0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

深度解析数据结构与算法篇之队列及环形队列的实现

Android编程精选 来源:编程学习总站 作者:写代码的牛顿 2021-06-18 10:07 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

01

队列简介

队列是种先进先出的数据结构,有个元素进入队列称为入对(enqueue),删除元素称为出队(dequeue),队列有对头(head)和对尾(tail),当有元素进入队列时就放在对尾的位置。

02

环形队列的实现

要想将元素放入队列我们必须知道对头和队尾,在队列长度不能无限大的条件下我们还要知道队列的最大容量,我们还想知道队列大小,所以队列内部能必须记录当前元素数量。现在我们定义一个结构体如下用于描述队列。

#define NAN (0xFFFFFE) typedef struct queue_arr{ int head; int tail; int cap; int size; int *arr; }queue_arr_t;

head是对列头,tail是对尾,cap记录队列的最大容量,size记录当前队列大小,arr指针保存一块内存的首地址。下面我们定义队列操作函数。

extern void queue_arr_init(queue_arr_t *q, int capacity); //队列初始化 extern void enqueue_arr(queue_arr_t *q, int data); //入队 extern int dequeue_arr(queue_arr_t *q); //出队 extern void queue_arr_destroy(queue_arr_t *q); //销毁队列 extern bool queue_arr_is_full(queue_arr_t *q); //判断队列是否满 extern bool queue_arr_is_empty(queue_arr_t *q); //判断队列是否为空 extern int queue_arr_length(queue_arr_t *q); //获取队列长度

队列初始化

void queue_arr_init(queue_arr_t *q, int capacity){ if(q == NULL || capacity 《= 0){ return; } q-》head = 0; q-》tail = 0; q-》size = 0; q-》arr = malloc(capacity * sizeof(int)); q-》cap = capacity; }

入队

void enqueue_arr(queue_arr_t *q, int data){ if(q == NULL){ return; } if(queue_arr_is_full(q)){ return; } //循环重用使用过的内存 if(q-》tail 》= q-》cap){ q-》tail = 0; } q-》arr[q-》tail++] = data; q-》size++; }

队列容量有限,在进行入队前一定对队列进行判断是否已满。

出队

int dequeue_arr(queue_arr_t *q){ if(q == NULL){ return NAN; } if(queue_arr_is_empty(q)){ return NAN; } if(q-》head 》= q-》cap){ q-》head = 0; } q-》size--; return q-》arr[q-》head++]; }

出队时一定要对队列进行判断是否已空。

判断队列是否已满

bool queue_arr_is_full(queue_arr_t *q){ if(q == NULL){ return false; } return q-》size == q-》cap ? true : false; }

判断队列是否已空

bool queue_arr_is_empty(queue_arr_t *q){ if(q == NULL){ return true; } return q-》size == 0 ? true : false; }

获取队列长度

int queue_arr_length(queue_arr_t *q){ if(q == NULL){ return 0; } return q-》size; }

销毁队列

void queue_arr_destroy(queue_arr_t *q){ if(q == NULL){ return; } if(q-》arr){ free(q-》arr); } q-》arr = NULL; q-》tail = 0; q-》head = 0; q-》cap = 0; q-》size = 0; }

03

链式队列的实现

为了避免队列元素的移动我们实现了环形队列,但是通过申请一块内存空间实现队列在队列大小未知的场景下无法满足我们不断加入元素进入队列的需求,这个时候就需要一种无需知道队列的最大容量,且能动态插入数据和取输出的队列实现。

我们知道链表能满足这样的需求,那么我们可以采用链表的实现方式实现队列。下面我们分别定义一个结构体用于描述链式队列和队列结点并声明队列操作函数。

typedef struct node{ int data; struct node *next; }node_t; typedef struct queue{ node_t *head; node_t *tail; }queue_t; extern void queue_init(queue_t *q); //队列初始化 extern void enqueue(queue_t *q, int data); //数据入队 extern int dequeue(queue_t *q); //数据出队列 extern int queue_length(queue_t *q); //获取队列长度 extern void queue_destroy(queue_t *q); //销毁队列

队列结点的next指针像单链表一样指向下一个结点,我们重点关注queue_t的定义,head是队列头,tail是对尾,有了对头和对尾就能快速的从对尾插入数据从对头取出数据。

队列初始化

void queue_init(queue_t *q){ if(q == NULL){ return; } q-》head = NULL; q-》tail = NULL; }

入队

元素每次进入队列都需要新建一个结点,为了方便我们定义一个新建结点函数new_node,函数实现如下:

static node_t *new_node(int data){ node_t *node = (node_t *)malloc(sizeof(node_t)); if(node == NULL){ return NULL; } node-》next = NULL; node-》data = data; return node; }

入队函数实现如下:

void enqueue(queue_t *q, int data){ if(q == NULL){ return; } node_t *node = new_node(data); //新建一个结点 if(node == NULL){ return; } if(q-》head == NULL && q-》tail == NULL){ //首次插入一个结点 q-》tail = node; q-》head = node; return; } q-》tail-》next = node; q-》tail = node; }

入队前要进行判断队列里是否有结点,这里通过队头和对尾是否为空指针进行判断,如果队列里没有结点则直接将对头和队尾指向插入的结点,否则结点则通过队尾tail获取到队列最后的结点,并将最后结点的next指针指向新插入的结点,最后更新队尾。

出队

每次从队列移除一个元素都需要释放队列结点内存,为了方便我们定义一个是否结点内存函数,函数实现如下:

static void free_node(node_t *node){ if(node == NULL){ return; } free(node); node-》next = NULL; }

出队函数实现如下:

int dequeue(queue_t *q){ if(q == NULL){ return NAN; } if(q-》head == NULL && q-》tail == NULL){ return NAN; } node_t *node = q-》head; int data = node-》data; if(q-》head-》next == q-》tail){ //只有一个结点 q-》head = NULL; q-》tail = NULL; }else{ //不止一个结点 q-》head = q-》head-》next; } node-》next = NULL; free_node(node); return data; }

出队同样要判断队列里是否有结点,没有结点则返回一个非法值,否则进行判断是否只有一个结点,若只有一个结点则直接返回头结点,并将对头和队尾指针置为空指针,否则获取队列头结点并更新对头指针。

获取队列长度

int queue_length(queue_t *q){ if(q == NULL){ return 0; } node_t *node = q-》head; if(node == NULL){ return 0; } int length = 1; while(node != q-》tail){ node = node-》next; length++; } return length; }

获取队列长度只要从队列头结点不断的遍历队列,判断当前结点是否已到队列尾部,最后返回队列长度。

销毁队列

void queue_destroy(queue_t *q){ if(q == NULL){ return; } node_t *node = q-》head; if(node == NULL){ return; } node_t *temp = NULL; while(node-》next != q-》tail){ temp = node; node = node-》next; free_node(temp); temp = NULL; } free_node(node); q-》tail = NULL; q-》head = NULL; }

04

结果验证

下面我们写一个小程序验证队列实现是否正确。

#include 《stdio.h》 #include “queue.h” int main() { queue_t queue; int i = 0; queue_init(&queue); //队列初始化 printf(“入队顺序 ”); for(i = 0; i 《 5; i++){ printf(“%d ,”, i + 1); enqueue(&queue, i + 1); } printf(“ ”); printf(“队列长度: %d ”, queue_length(&queue)); printf(“取出队列一个数据:%d ”, dequeue(&queue)); printf(“取出队列一个数据:%d ”, dequeue(&queue)); printf(“队列长度: %d ”, queue_length(&queue)); printf(“销毁队列 ”); queue_destroy(&queue); printf(“ ”); printf(“大小固定的队列测试 ”); queue_arr_t queue2; queue_arr_init(&queue2, 6); printf(“入队顺序 ”); for(i = 10; i 《 16; i++){ printf(“%d ,”, i); enqueue_arr(&queue2, i); } printf(“ ”); if(queue_arr_is_full(&queue2)){ printf(“队列已满 ”); }else{ printf(“队列未满 ”); } printf(“取出队列一个数据:%d ”, dequeue_arr(&queue2)); printf(“取出队列一个数据:%d ”, dequeue_arr(&queue2)); if(queue_arr_is_full(&queue2)){ printf(“队列已满 ”); }else{ printf(“队列未满 ”); } printf(“队列长度是:%d ”, queue_arr_length(&queue2)); printf(“销毁队列 ”); queue_arr_destroy(&queue2); if(queue_arr_is_empty(&queue2)){ printf(“队列已空 ”); }else{ printf(“队列未空 ”); } return 0; }

编译输出如下:

入队顺序 1 ,2 ,3 ,4 ,5 , 队列长度: 5 取出队列一个数据:1 取出队列一个数据:2 队列长度: 3 销毁队列 大小固定的队列测试 入队顺序 10 ,11 ,12 ,13 ,14 ,15 , 队列已满 取出队列一个数据:10 取出队列一个数据:11 队列未满 队列长度是:4 销毁队列 队列已空

输出完全正确。

编辑:jq

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 数据
    +关注

    关注

    8

    文章

    7315

    浏览量

    93999
  • 函数
    +关注

    关注

    3

    文章

    4406

    浏览量

    66858

原文标题:数据结构与算法篇-队列

文章出处:【微信号:AndroidPush,微信公众号:Android编程精选】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    NVMe高速传输摆脱XDMA设计54:如何测试队列管理功能2

    , 表示操作顺序错误; 创建 I/O 完成队列和提交队列后, 先删除完成队列, 返回错误值为 8, 表示操作顺序错误。 打印信息显示测试结果与预期设计功能一致, 成功执行了队列的创建和
    发表于 12-10 08:33

    NVMe高速传输摆脱XDMA设计53:如何测试队列管理功能

    管理功能, 对队列的创建、 删除功能和队列管理边界进行了测试。 创建队列测试打印信息如图1 所示, 在初始化完成后, 创建 I/O 完成队列和提交
    发表于 12-09 08:21

    优先级队列介绍

    队列(Queue)的知识点:「概念」:队列是一种先进先出(FIFO)的数据结构,类似于排队的概念。「基本操作」:enqueue(item): 将元素添加到队列的末尾。dequeue()
    发表于 11-26 07:56

    基于环形队列的UART收发回显实验

    问题。在本实验中,我们使用环形队列实现实验1的串口收发回显,将串口接收到的数据暂存在队列中,待完成一次接收后再将
    的头像 发表于 10-27 13:51 1711次阅读
    基于<b class='flag-5'>环形</b><b class='flag-5'>队列</b>的UART收发回显实验

    NVMe高速传输摆脱XDMA设计40:队列管理功能验证与分析4

    本文主要交流NVMe设计思路,在本博客已给出相关博文几十,希望对初学者有用。注意这里只是抛砖引玉,切莫认为参考这就可以完成商用IP设计。有需求者请先看下面B站联系后再讨论。 (3) 删除队列功能
    发表于 10-22 10:14

    NVMe高速传输摆脱XDMA设计39:队列管理功能验证与分析3

    本文主要交流NVMe设计思路,在本博客已给出相关博文九十多,希望对初学者有用。注意这里只是抛砖引玉,切莫认为参考这就可以完成商用IP设计。 测试步骤 4 关键信号波形如图1 所示。 创建深度
    发表于 10-20 16:01

    NVMe高速传输摆脱XDMA设计38:队列管理功能验证与分析2

    波形如图1 所示。 创建深度为 1024 的 I/O 提交队列, 由于支持的最大队列深度为 1023, 所以创建返回状态 cr_status 值为 4, 表示创建
    发表于 10-15 08:14

    NVMe高速传输摆脱XDMA设计37:队列管理功能验证与分析1

    本文主要交流设计思路,在本博客已给出相关博文九十多,希望对初学者有用。注意这里只是抛砖引玉,切莫认为参考这就可以完成商用IP设计。若有产品或项目需求,请看B站视频后联系 队列管理功能主要包含创建
    发表于 10-13 11:17

    NVMe IP高速传输却不依赖XDMA设计九:队列管理模块(上)

    这是采用PCIe设计NVMe,并非调用XDMA方式,后者在PCIe4.0时不大方便,故团队直接采用PCIe设计,结合UVM验证加快设计速度。 队列管理模块采用队列的存储与控制分离的设计结构
    的头像 发表于 08-04 09:53 603次阅读
    NVMe IP高速传输却不依赖XDMA设计<b class='flag-5'>之</b>九:<b class='flag-5'>队列</b>管理模块(上)

    NVMe高速传输摆脱XDMA设计十:队列管理模块设计(下)

    ,通过修改表单的信息便可以修改队列数量和深度。在实际应用中,当出现大量随机数据读写请求时,可以通过修改表单增加队列数量和深度,增强随机读写性
    发表于 07-30 16:27

    NVMe高速传输摆脱XDMA设计九:队列管理模块设计(上)

    设计,结合UVM验证加快设计速度。队列管理模块采用队列的存储与控制分离的设计结构,如图1所示为队列管理模块的结构框图。 图1
    发表于 07-27 17:41

    RabbitMQ消息队列解决方案

    在现代分布式系统架构中,消息队列作为核心组件,承担着系统解耦、异步处理、流量削峰等重要职责。RabbitMQ作为一款成熟的消息队列中间件,以其高可用性、高可靠性和丰富的特性,成为众多企业的首选方案。本文将从运维工程师的角度,详细阐述RabbitMQ从单机部署到集群搭建的完
    的头像 发表于 07-08 15:55 439次阅读

    程序设计与数据结构

    的地址)出发,采用推导的方式,深入浅出的分析了广大C程序员学习和开发中遇到的难点。 2. 从方法论的高度对C语言在数据结构算法方面的应用进行了深入讲解和阐述。 3. 讲解了绝大多数C程序员开发
    发表于 05-13 16:45

    NVME控制器队列管理模块

    队列管理模块是整个NVMe Host控制器的核心模块,该模块实现了提交队列与完成队列的管理,多队列请求的仲裁判决等功能。
    发表于 05-03 20:19

    NVME控制器队列管理模块

    队列管理模块是整个NVMe Host控制器的核心模块,该模块实现了提交队列与完成队列的管理,多队列请求的仲裁判决等功能。
    的头像 发表于 05-03 15:32 429次阅读
    NVME控制器<b class='flag-5'>之</b><b class='flag-5'>队列</b>管理模块