【数据结构】队列(环形缓冲区)的实现
在学习驱动的过程中,接触到了环形缓冲区的概念,发现这个缓冲区和数据结构中的队列具有惊人的相似之处,因此借此来复习相关知识
如果应用层来不及读取数据时,我们可以先将数据放入环形缓冲区中用来记录数据,防止数据丢失。当然,缓冲区越大,那么可以缓存的数据就越多。
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
posted on 2024-10-23 16:39 唐同学Maydayz 阅读(12) 评论(0) 编辑 收藏 举报