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

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

3天内不再提示

一种数组环形队列的数据结构

技术让梦想更伟大 来源:CSDN-阏男秀 2023-04-26 09:17 次阅读

概述

一种更好的计算队尾指针的方法。

队尾指针新算法

一个新的计算队尾指针的公式:

模拟环形队列的线性表长度是N,队头指针为head,队尾指针为tail,则每增加一条记录,就可以用一下方法计算新的队尾指针: tail = (tail + 1) % N

780c9d90-e3b6-11ed-ab56-dac502259ad0.jpg

环形队列示意图

思考

但是,我在移植到8266的代码时,发现有点问题。

第一,head和tail应该是存储该数组的下标而不是一个指向该数组元素的指针。如果tail是指针,那么 tail本质上是一个内存地址,*tail是指向该数组的某一元素。那么这算式tail = (tail + 1) % N其实是对某数组元素的内存地址+1,然后在用N求余。 所以head和tail应该是保存该数组的下标,而不是指向该数组元素的指针。

第二,由于原先的代码,其队尾指针总是指向最后一个入队元素的下一个元素,而《算》给出的队列,队尾指针总是指向最后一个入队的元素。如上图,一个N=12的环形队列,原先的代码tail是指向第8个,《算》是指向第7个,由于我是在8266的示例代码上修改的,所以《算》给出的队尾指针算法需要调整一下:

//元素入队之后
tail++;//tail指向最后一个入队的下一个元素
tail=tail%N;//重新计算tail的数值123

新的数据结构

那么我就开始定义新的数据结构了,原先的数据结构是这样的

typedefstruct{
uint8_t*p_o;//指向原点的指针,用来数组首地址
uint8_t*volatilep_r;//读取指针,相当于head
uint8_t*volatilep_w;//写入指针,相到于tail
volatileint32_tfill_cnt;//队列计数
int32_tsize;//缓冲区的大小
}RINGBUF;

新的数据结构:

typedefstruct{
char*buf;//指向队列数组的指针
unsignedintlength;//数组长度
unsignedinthead;//队头,存储数组下标
unsignedinttail;//队尾,存储数组下标
intfill_cnt;//队列计数
}RINGBUF;

判断是否空队列

一开始,本来想用if(head==tail)来判断队列是否为空的,但是由于tail保存的是入队元素的下一个数组下标,当队列填满的时候,tail的下标正好等于head,所以不能通过if(head==tail)来判断队列是否为空,

完整代码

下面是完整的数组环形队列代码,运行环境是VS2015,主函数里进行了简单的测试:

// RingBuf.cpp :定义控制台应用程序的入口点。
//

#include"stdafx.h"

typedefstruct{
char*buf;//指向队列数组的指针
unsignedintlength;//长度
unsignedinthead;//队头
unsignedinttail;//队尾
intfill_cnt;//队列计数
}RINGBUF;

intRINGBUF_Init(RINGBUF*r,chararray[],size_tlen)
{
if(len<2 || array==NULL){
        return false;
    }

    r->buf=array;
r->length=len;
r->fill_cnt=0;
r->head=r->tail=0;
returntrue;
}

intRINGBUF_Put(RINGBUF*r,chardata)
{
//当tail+1等于head时,说明队列已满
if(r->fill_cnt>=r->length){
printf("BUFFULL!
");
returnfalse;//如果缓冲区满了,则返回错误
}

r->buf[r->tail]=data;
r->tail++;
r->fill_cnt++;
//计算tail是否超出数组范围,如果超出则自动切换到0
r->tail=r->tail%r->length;
returntrue;
}

intRINGBUF_Get(RINGBUF*r,char*c,unsignedintlength)
{
//当tail等于head时,说明队列空
if(r->fill_cnt<=0) {
        printf("BUF EMPTY!
");
        return false;
    }

    //最多只能读取r->length长度数据
if(length>r->length){
length=r->length;
}

inti;
for(i=0;ifill_cnt--;
*c=r->buf[r->head++];//返回数据给*c
*c++;
//计算head自加后的下标是否超出数组范围,如果超出则自动切换到0
r->head=r->head%r->length;
}
returntrue;
}



#defineBUF_LEN5
RINGBUFBUFF;
charbuf[BUF_LEN];

intmain()
{
RINGBUF_Init(&BUFF,buf,sizeof(buf));

printf("1、逐个读取数据测试
");
intlength=5;
for(inti=0;i< length; i++) {
        RINGBUF_Put(&BUFF, i);
    }

    char data;

    length = 5;
    for (int i = 0; i < length; i++) {
        RINGBUF_Get(&BUFF, &data, 1); //从BUFF读取1个字节
        printf("每次读取1个字节:buf pop : %d 
", data);  //打印该字节
    }

    printf("
2、一次性读取测试
");
    length = 5;
    for (int i = 0; i < length; i++) {
        RINGBUF_Put(&BUFF, '1' + i);
    }
    char data2[11] = { 0 };
    RINGBUF_Get(&BUFF, data2, 5);
    printf("一次性读取5个字节:buf pop : %s 
", data2);  //打印该字节

    printf("
3、放入超过缓冲区长度(BUF_LEN+1)数据测试:
");
    length = BUF_LEN + 1;
    for (int i = 0; i < length; i++){
        RINGBUF_Put(&BUFF, '1'+i);
    }

    char data3[BUF_LEN+1] = { 0 };
    RINGBUF_Get(&BUFF, data3, BUF_LEN + 1);
    printf("一次性读取(BUF_LEN+1)个字节测试:buf pop : %s 
", data3);  //打印该字节

    //4、测试读取空缓冲区
    printf("
4、读取空缓冲区测试:
");
    RINGBUF_Get(&BUFF, data3, 2); //从BUFF读取2个字节

    return 0;
}

控制台打印信息如下:

1、逐个读取数据测试
每次读取1个字节:buf pop : 0
每次读取1个字节:buf pop : 1
每次读取1个字节:buf pop : 2
每次读取1个字节:buf pop : 3
每次读取1个字节:buf pop : 4

2、一次性读取测试
一次性读取5个字节:buf pop : 12345

3、放入超过缓冲区长度(BUF_LEN+1)数据测试:
BUF FULL!
一次性读取(BUF_LEN+1)个字节测试:buf pop : 12345

4、读取空缓冲区测试:
BUF EMPTY!
请按任意键继续…

后话

由于存在几种队尾指向元素的方式,以上代码是还可以在修改优化一下的。

《算》的代码是不需要考虑队列是否满了,他只需要直接覆盖旧的元素即可,我的需求是需要判断队列是否填满,以免旧的元素被覆盖。

审核编辑:汤梓红

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

    关注

    23

    文章

    4544

    浏览量

    91995
  • 指针
    +关注

    关注

    1

    文章

    475

    浏览量

    70457
  • 代码
    +关注

    关注

    30

    文章

    4665

    浏览量

    67744
  • 数据结构
    +关注

    关注

    3

    文章

    568

    浏览量

    40024
  • 数组
    +关注

    关注

    1

    文章

    411

    浏览量

    25816

原文标题:一种数组环形队列的数据结构

文章出处:【微信号:技术让梦想更伟大,微信公众号:技术让梦想更伟大】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    嵌入式常用数据结构------队列操作简介

    嵌入式常用数据结构------队列操作简介队列是嵌入式软件中常用的一种数据结构。什么是队列呢?在生活中,我们都知道,买东西时要排队,比如最近
    发表于 06-17 17:30

    收藏 | 程序员面试,你必须知道的8大数据结构

    数据结构首先列出些最常见的数据结构,我们将逐说明:数组队列链表树图字典树(这是
    发表于 09-30 09:35

    常见的数据结构

    胡乱的,这就要求我们选择一种好的方式来存储数据,而这也是数据结构的核心内容。数据存储直以来大家面对的
    发表于 05-10 07:58

    数据结构队列顺序及其构造

    数据结构队列顺序队列构造顺序队列顺序队列的初始化判断队列是否满判断
    发表于 12-17 06:11

    实现队列环形缓冲的方法

    串口队列环形缓冲区队列串口环形缓冲的好处代码实现队列  要实现队列
    发表于 02-21 07:11

    环形队列的相关资料分享

    的小伙伴,对队列肯定不会陌生,队列相对来说是比较简单的数据结构,典型特点是FIFO,即First in First out,先进先出,就像我们日常排队买票样,先到的人先买票,先从购票
    发表于 02-23 06:10

    数据结构的简介和线性表及栈队列数组的详细说明

    两项基本任务:数据表示,数据处理 软件系统生存期:软件计划,需求分析,软件设计,软件编码,软件测试,软件维护 由一种逻辑结构组基本运
    发表于 01-06 08:00 0次下载

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

    01 — 队列简介 队列先进先出的数据结构,有个元素进入队列称为入对(enqueue),删除元素称为出队(dequeue),
    的头像 发表于 06-18 10:07 1764次阅读

    STM32串口环形缓冲--使用队列实现(开放源码)

    串口队列环形缓冲区队列串口环形缓冲的好处代码实现队列  要实现队列
    发表于 12-24 19:04 28次下载
    STM32串口<b class='flag-5'>环形</b>缓冲--使用<b class='flag-5'>队列</b>实现(开放源码)

    SystemVerilog中可以嵌套的数据结构

    SystemVerilog中除了数组队列和关联数组数据结构,这些数据结构还可以嵌套。
    的头像 发表于 11-03 09:59 1452次阅读

    嵌入式环形队列和消息队列的实现

    嵌入式环形队列和消息队列是实现数据缓存和通信的常见数据结构,广泛应用于嵌入式系统中的通信协议和领域。
    的头像 发表于 04-14 11:52 1364次阅读

    数据结构解决滑动窗口问题

    前文用 [单调栈解决三道算法问题]介绍了单调栈这种特殊数据结构,本文写个类似的数据结构「单调队列」。 也许这种数据结构的名字你没听过,其
    的头像 发表于 04-19 10:50 559次阅读
    <b class='flag-5'>数据结构</b>解决滑动窗口问题

    redis的五种数据类型底层数据结构

    Redis是一种内存数据存储系统,支持多种数据结构。这些数据结构不仅可以满足常见的存储需求,还能够通过其底层数据结构提供高效的操作和查询。以
    的头像 发表于 11-16 11:18 599次阅读

    redis数据结构的底层实现

    Redis是一种内存键值数据库,常用于缓存、消息队列、实时数据分析等场景。它的高性能得益于其精心设计的数据结构和底层实现。本文将详细介绍Re
    的头像 发表于 12-05 10:14 508次阅读

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

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