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

    文章

    7349

    浏览量

    95025
  • 函数
    +关注

    关注

    3

    文章

    4422

    浏览量

    67855

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

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

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

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    RDMA设计25:队列管理模块发送模块详细设计分析

    发送队列存储为所有发送队列共用的存储空间,根据用户环境和开发板环境不同可由 BRAM、URAM 或 LUTRAM 实现。发送队列管理单元则负责管理这个存储空间,并处理用户指令和发送
    的头像 发表于 01-25 16:27 5227次阅读
    RDMA设计25:<b class='flag-5'>队列</b>管理模块<b class='flag-5'>之</b>发送模块详细设计分析

    RDMA设计27:队列管理模块设计完成模块详细分析

    。 (3)完成队列 完成队列的管理由完成条目解析单元和异常完成条目处理单元组成。完成条目解析单元中只设置了一个虚拟完成队列,使用这样的
    发表于 01-23 08:52

    RDMA设计26:队列管理模块设计接收队列模块详细分析

    本文主要交流设计思路,在本博客已给出相关博文100多,希望对初学者有用。注意这里只是抛砖引玉,切莫认为参考这就可以完成商用IP设计。 (2)接收队列 接收队列由一个接收队列管理单元组
    发表于 01-22 09:03

    RDMA设计24:队列管理模块设计

    队列管理模块采用管理与存储分离的结构进行设计,由发送队列存储、发送队列管理、接收队列管理、完成条目解析
    的头像 发表于 01-20 11:45 1557次阅读
    RDMA设计24:<b class='flag-5'>队列</b>管理模块设计

    RDMA设计18:队列管理模块设计3

    本文主要交流设计思路,在本博客已给出相关博文140多,希望对初学者有用。注意这里只是抛砖引玉,切莫认为参考这就可以完成商用IP设计。 (3)完成队列 完成队列的管理由完成条目解析单元
    发表于 01-05 09:04

    C语言的循环队列

    data; } return -1; // Buffer is empty } 循环队列是一种高效的数据结构,适用于缓冲区和数据流应用,例如串口通信接收缓冲。
    发表于 12-12 08:28

    优先级队列介绍

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

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

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

    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高速传输摆脱XDMA设计十:队列管理模块设计(下)

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

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

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

    NVME控制器队列管理模块

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

    NVME控制器队列管理模块

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