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

    文章

    7193

    浏览量

    89819
  • 函数
    +关注

    关注

    3

    文章

    4350

    浏览量

    63051

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

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

收藏 人收藏

    评论

    相关推荐

    JavaWeb消息队列使用指南

    在现代的JavaWeb应用中,消息队列(Message Queue)是一种常见的技术,用于异步处理任务、解耦系统组件、提高系统性能和可靠性。 1. 消息队列的基本概念 消息队列是一种应用程序对应
    的头像 发表于 11-25 09:27 198次阅读

    探索字节队列的魔法:多类型支持、函数重载与线程安全

    数据结构,它能够高效地存储和管理数据流。通过使用字节队列,我们可以灵活地处理不同类型的数据、确保数据的完整性,并在多线程环境中安全地进行操
    的头像 发表于 11-15 01:08 920次阅读
    探索字节<b class='flag-5'>队列</b>的魔法:多类型支持、函数重载与线程安全

    为什么同一个队列引用的全局变量,运行在两个子vi中发现队列数据丢失了

    我创建了一个队列,然后将队列引用做了个全局变量,运行在两个子vi中,一个是只入队列,另一个是只出队列。但我发现,一个字vi数据
    发表于 11-14 11:47

    视觉软件HALCON的数据结构

    在研究机器视觉算法之前,我们需要先了解机器视觉应用中涉及的基本数据结构。Halcon数据结构主要有图像参数和控制参数两类参数。图像参数包括:image、region、XLD,控制参数包括:string、integer、real、
    的头像 发表于 11-14 10:20 583次阅读
    视觉软件HALCON的<b class='flag-5'>数据结构</b>

    嵌入式环形队列与消息队列实现原理

    嵌入式环形队列,也称为环形缓冲区或循环队列,是一种先进先出(FIFO)的数据结构,用于在固定大小的存储区域中高效地存储和访问
    的头像 发表于 09-02 15:29 698次阅读

    玩转RT-Thread消息队列的应用

    不同来源的数据,确保系统的稳定性和响应速度。一、设计消息结构二、创建消息队列在service.c文件中,我们需要创建一个消息队列来存放这些消息,并在处理线程中接收和
    的头像 发表于 07-23 08:11 684次阅读
    玩转RT-Thread<b class='flag-5'>之</b>消息<b class='flag-5'>队列</b>的应用

    嵌入式实时操作系统中的队列管理与应用

    任务 A 将信息存入队列,任务B以先进先出的方式提取信息。队列通常应足够大,可以承载许多数据,而不仅仅承载单个数据项。因此,它可以充当缓冲或暂存器,为管道提供灵活性。
    发表于 04-30 14:27 698次阅读
    嵌入式实时操作系统中的<b class='flag-5'>队列</b>管理与应用

    Freertos队列项里的字节长度是否可以获取?

    最近刚学Freertos, 看到可以获取Freertos队列长度,但是队列项里的字节长度是否可以获取? 因为项目中队列中会存放不定长字节,需要对队列中的
    发表于 04-29 07:17

    OpenHarmony语言基础类库【@ohos.util.Queue (线性容器Queue)】

    Queue的特点是先进先出,在尾部增加元素,在头部删除元素。根据循环队列数据结构实现
    的头像 发表于 04-27 21:20 385次阅读
    OpenHarmony语言基础类库【@ohos.util.Queue (线性容器Queue)】

    freertos队列错乱是什么原因导致的?

    最近调试//发送两个队列 xResult = xSemaphoreTake(xSemaphore, (TickType_t)1); if(xResult == pdTRUE
    发表于 04-26 06:20

    用FreeRTOS使用队列怎么发送一个结构体呢?

    怎么使用队列,发送一个12个字节的结构体呢? osEvent osMessageGet (osMessageQId queue_id, uint32_t millisec
    发表于 04-17 07:35

    进程间通信的消息队列介绍

    消息队列是一种非常常见的进程间通信方式。
    的头像 发表于 04-08 17:27 355次阅读

    MCU专属队列功能模块QueueForMcu应用

    当需要从队列头部获取多个数据,但又不希望数据队列中删除时,可以使用 Queue_Peek_Array 函数来实现,该函数的参数与返回值与
    发表于 03-20 11:44 570次阅读
    MCU专属<b class='flag-5'>队列</b>功能模块<b class='flag-5'>之</b>QueueForMcu应用

    TC399 adc能添加到同一个队列中并得到结果吗?加入队列是否有任何限制?

    你好,,我正在尝试实现 ADC。 在我的项目中,我们使用 AN0...AN60 进行不同的电压监控。 从数据表中可以发现,这些是不同的通道和组。 在一个示例程序中,它显示将第 0 组的 3 个通道
    发表于 03-04 06:33

    矢量与栅格数据结构各有什么特征

    数据结构是使用点、线和面等基本几何图形来描述和表示地理对象的一种方法。它们由离散的几何对象和与相关的属性数据组成。矢量数据中的点表示一个特定的地理位置,线表示两个或多个点之间的连接,
    的头像 发表于 02-25 15:06 2802次阅读