首页 > 其他分享 >【数据结构】队列(环形缓冲区)的实现

【数据结构】队列(环形缓冲区)的实现

时间:2024-10-23 16:35:13浏览次数:1  
标签:队列 sq fifo front int 缓冲区 数据结构 rear

在学习驱动的过程中,接触到了环形缓冲区的概念,发现这个缓冲区和数据结构中的队列具有惊人的相似之处,因此借此来复习相关知识

如果应用层来不及读取数据时,我们可以先将数据放入环形缓冲区中用来记录数据,防止数据丢失。当然,缓冲区越大,那么可以缓存的数据就越多。

1. 队列的定义

队列是限制在两端进行插入操作和删除操作的线性表。特点 :先进先出(FIFO)

  • 允许进行存入操作的一端称为“队尾”
  • 允许进行删除操作的一端称为“队头”
  • 当线性表中没有元素时,称为“空队”

顺序队列 的本质就是一个数组,只不过限制在一端进行插入(入队),一端限制删除(出队)

2. 顺序队列的数据结构

#define MaxSize 8
typedef struct {
	int data[MaxSize];
	int front,rear;
} SqQueue;

3.队列相关函数实现

3.1 队列初始化

初始时,默认rear和front都指向下标0的位置。

void fifo_init(SqQueue *sq)
{
	sq.rear = sq.front = 0;
}

3.2 判断队列是否为空

初始时,队列的 rear == front 队列为空,当经过几次入队和出队操作后,队列空的条件依然是:rear == front

int fifo_empty(SqQueue *sq)
{
	if(sq.rear == sq.front)
	{
		return 1;
	}
	return 0;
}

3.3 入队

入队时,需要判断是否队列已满,如果满了就不能入队。前面已经已经指定过 rear == front ,假设让队列全部放满元素,那么rear和front又指向同一个位置。这种情况下,我们选择牺牲掉一个存储单元。当 rear+1指向 front位置时,就表示队列已经满了。

int fifo_put(SqQueue *sq, int value)
{
	if((sq.rear + 1) % MaxSize == sq.front)//队列满
	{
		return -1;
	}
	sq.data[sq.rear] = value;
	sq.rear = (sq.rear + 1) % MaxSize;
	return 0;
}

3.4 出队

出队时,需要判断队列是否为空,只有有数据的时候才可以出队。

int fifo_get(SqQueue *sq)
{
	int temp_data;
	if(sq.rear == sq.front)//队列空
	{
		return -1;
	}
	int fifo_out_data = sq.data[sq.front];
	sq.front = (sq.front + 1) % MaxSize;
	return fifo_out_data;
}

4.测试

模拟一个缓冲区,假设接收处理数据比写入数据的速度慢,可以体现环形缓冲区的好处。

#include <stdio.h>
#include <unistd.h>

/* 定义队列 */
#define MaxSize 8
typedef struct {
	int data[MaxSize];
	int front,rear;
} SqQueue;

/* 队列初始化 */
void fifo_init(SqQueue *sq)
{
	sq->rear = sq->front = 0;
}

/* 判断队列是否为空 */
int fifo_empty(SqQueue *sq)
{
	if(sq->rear == sq->front)
	{
		return 1;
	}
	return 0;
}

/* 入队 */
int fifo_put(SqQueue *sq, int value)
{
	if((sq->rear + 1) % MaxSize == sq->front)//队列满
	{
		return -1;
	}
	sq->data[sq->rear] = value;
	sq->rear = (sq->rear + 1) % MaxSize;
	return 0;
}

/* 出队 */
int fifo_get(SqQueue *sq)
{
	int temp_data;
	if(sq->rear == sq->front)//队列空
	{
		return -1;
	}
	int fifo_out_data = sq->data[sq->front];
	sq->front = (sq->front + 1) % MaxSize;
	return fifo_out_data;
}

int main(int argc,char **argv)
{
	SqQueue sq;
	fifo_init(&sq);

	int uart_data[10] = {1, 2, 3, 4, 5, 6, 7 ,8 ,9 ,10};
	int count = sizeof(uart_data)/sizeof(uart_data[0]);

	for(int i = 0; i < count; i++)
	{
		fifo_put(&sq,uart_data[i]);
		printf("fifo_put:%d rear = %d\n", uart_data[i], sq.rear);
		if(i % 2 == 0)
		{
			int ret = fifo_get(&sq);
			printf("fifo_get:%d front = %d\n", ret, sq.front);
		}
		sleep(1);
	}

	while(!fifo_empty(&sq))
	{
		int ret = fifo_get(&sq);
		printf("fifo_get:%d front = %d\n", ret, sq.front);
	}
	return 0;
}

结果输出:

fifo_put:1 rear = 1
fifo_get:1 front = 1
fifo_put:2 rear = 2
fifo_put:3 rear = 3
fifo_get:2 front = 2
fifo_put:4 rear = 4
fifo_put:5 rear = 5
fifo_get:3 front = 3
fifo_put:6 rear = 6
fifo_put:7 rear = 7
fifo_get:4 front = 4
fifo_put:8 rear = 0
fifo_put:9 rear = 1
fifo_get:5 front = 5
fifo_put:10 rear = 2
fifo_get:6 front = 6
fifo_get:7 front = 7
fifo_get:8 front = 0
fifo_get:9 front = 1
fifo_get:10 front = 2

当处理完数据时,front == rear 缓冲区为空。

5. 改进方案

为了不牺牲一个存储单元,其实有很多办法,下面总结一些方案:

5.1 增加一个成员表示队列当前长度

#define MaxSize 10
typedef struct {
	int data[MaxSize];
	int front,rear;
	int size;//队列当前长度
} SqQueue;

初始化时

rear = front = 0;
size = 0;

插入成功:size++;
删除成功:size--;
队列空:size = 0;
队列满:size = MaxSize;

5.2 增加一个标记位,记录最近一次操作

在队列的结构体中,增加一个 tag 成员

  • 当最近一次进行的删除操作 tag = 0
  • 当最近一次进行的插入操作 tag = 1
  • 只有删除操作,才可能导致队空
  • 只有插入操作,才可能导致队满
#define MaxSize 10
typedef struct {
	ElemType data[MaxSize];
	int front,rear;
	int tag;//最近进行的是删除/插入
} SqQueue;

初始时

rear = front = 0;
tag = 0;

每次删除操作成功时,都令 tag = 0
每次插入操作成功时,都令 tag = 1
队列空:front == rear && tag == 0
队列满:front == rear && tag == 1

标签:队列,sq,fifo,front,int,缓冲区,数据结构,rear
From: https://www.cnblogs.com/cifeng79/p/18497702

相关文章

  • 【数据结构与算法】之链表经典算法大集合
    本文主要内容是几个关于链表的初级经典算法的分享,都采用Java语言实现,话不多说,立马开始!注意:以下代码有关链表的算法实现均基于以下链表节点类://链表节点类publicclassListNode{intval;ListNodenext;ListNode(){}ListNode(intval){this.val=v......
  • Ruby数据结构介绍
    Ruby中的String 对象存储并操作一个或多个字节的任意序列,通常表示那些代表人类语言的字符。最简单的字符串是括在单引号(单引号字符)内。在引号标记内的文本是字符串的值:'ThisisasimpleRubystringliteral'如果您需要在单引号字符串内使用单引号字符,那么需要在单引号......
  • 六种概率数据结构的详细解释及应用场景
    1/BloomFilter用途:测试一个元素是否可能在一个集合中。原理:BloomFilter使用多个哈希函数将元素映射到一个位数组上。如果所有对应的位都被设置为1,则认为该元素可能在集合中。优点:非常节省空间,因为不需要存储实际的元素,只需存储位图信息。应用:在数据库查询优化、网页缓......
  • 【趣学C语言和数据结构100例】
    #1024程序员节|征文#【趣学C语言和数据结构100例】问题描述56.设将n(n>1)个整数存放到区带头结点处单链表乚中,设计算法将L中保存的序列循环石移k(0<k<n)个位置。例如,若k=1,则将链表(0,1,2,3}变为{3,0,1,2}57.设有一个带头结点的非循环双链表L,其每个结点中除有pre、da......
  • 【Python-AI篇】数据结构和算法
    1.算法概念1.1什么是数据结构存储,组织数据的方式1.2什么是算法实现业务目的的各种方法和思路算法是独立的存在,只是思想,不依附于代码和程序,可以使用不同语言实现(java,python,c)1.2.1算法的五大特性输入:算法具有0个或多个输入输出:算法至少有1个或多个输出有穷性:算法......
  • 队列以及循环队列及其基本操作
    和栈相反,队列是一种先进先出的数据结构,他在表尾插入元素,表头删除元素。队列也分为链队列以及顺序队列两种,链队列动态分配空间,不用担心空间不足,顺序队列简单易懂,操作方便,但是空间利用率低,所以我们一般使用链式队列结构。链式队列对顺序队列进行初始化,顺序队列分配空间类似于......
  • 数据结构 链表 C语言
    数据结构第二章的链表//线性表的链式存储#include<stdlib.h>#include<stdio.h>typedefintElemType;typedefstructnode{ElemTypedata;structnode*next;}Node,*LinkList;//初始化空的单链表voidInitList(LinkList*L){*L=(LinkLis......
  • 232. 用栈实现队列
    classMyQueue{public:MyQueue(){}voidpush(intx){s1.push(x);}intpop(){intret;if(!empty()){if(!s2.empty()){ret=s2.top();s2.pop();......
  • 一文搞定栈与队列
    栈与队列基础入门栈常用操作栈的实现基于链表实现基于数组实现 队列常用操作队列的实现基于链表实现基于数组实现 双向队列常用操作重点知识栈与队列的区别怎么用两个队列去实现栈 两个栈实现一个队列相关题解leetcode20.有效的括号法一:栈leetcode155.......
  • 数据结构实验1
    1//1.线性表顺序表的初始化ok2//2.输出ok,插入ok,删除ok,逆转ok,销毁ok,置空表ok(两种插入,两种删除)3//3.求表长ok,查找元素ok,判断是否为空ok4//4.实现顺序表元素的逆转并输出结果ok5//5.顺序表合并:动态创建两个有序的顺序表,输出合并后的顺序表6#include<cstdi......