Circular Queue Implementation Principle

目录

1. 引言
2. 环形队列的实现原理
3. 环形队列编程实现

1. 引言

环形队列是在实际编程极为有用的数据结构,它有如下特点

1. 它是一个首尾相连的FIFO(First In First Out 队列)的数据结构
2. 采用数组的线性空间,数据组织简单
3. 能很快知道队列是否满、或是否空
4. 能以很快的速度、几乎无锁的方式(只在写满或为空的情况下需要加锁实现互斥同步)来存取数据 

因为环形队列简单高效的原因,甚至在硬件都实现了环形队列,环形队列广泛用于网络数据收发,和不同程序间数据交换(比如内核与应用程序大量交换数据,从硬件接收大量数据)均使用了环形队列

0x1: FIFO队列

First Input First Output的缩写,先入先出队列,这是一种传统的按序执行方法,先进入的指令先完成并引退,跟着才执行第二条指令

1. FIFO队列不对报文进行分类,当报文进入接口的速度大于接口能发送的速度时,FIFO按报文到达接口的先后顺序让报文进入队列,同时,FIFO在队列的出口让报文按进队的顺序出队,先进的报文将先出队,后进的报文将后出队
2. FIFO队列具有处理简单,开销小的优点。但FIFO不区分报文类型,采用尽力而为的转发模式,使对时间敏感的实时应用(如VoIP)的延迟得不到保证,关键业务的带宽也不能得到保证
3. FIFO一般用于不同时钟域之间的数据传输,比如FIFO的一端是AD数据采集,另一端是计算机的PCI总线
    1) 假设其AD采集的速率为16位 100K SPS,那么每秒的数据量为100K×16bit=1.6Mbps
    2) PCI总线的速度为33MHz,总线宽度32bit,其最大传输速率为1056Mbps
在两个不同的时钟域间就可以采用FIFO来作为数据缓冲。另外对于不同宽度的数据接口也可以用FIFO,例如单片机位8位数据输出,而DSP可能是16位数据输入,在单片机与DSP连接时就可以使用FIFO来达到数据匹配的目的 

根据FIFO工作的时钟域,可以将FIFO分为两类

1. 同步FIFO
同步FIFO是指读时钟和写时钟为同一个时钟。在时钟沿来临时同时发生读写操作,共同争用同一个FIFO队列

2. 异步FIFO
异步FIFO是指读写时钟不一致,读写时钟是互相独立的,可以一定程度上提高FIFO的利用效率,同时也会带来同步的问题

FIFO设计的难点在于怎样判断FIFO的空/满状态。为了保证数据正确的写入或读出,而不发生溢出或读空的状态出现,必须保证FIFO在满的情况下,不能进行写操作。在空的状态下不能进行读操作。怎样判断FIFO的满/空就成了FIFO设计的核心问题,而循环队列是一个很好的算法思路

Relevant Link:

http://baike.baidu.com/view/736423.htm?fromtitle=FIFO&fromid=64838&type=syn

2. 环形队列的实现原理

内存上没有环形的结构,因此环形队列实上是数组的线性空间来实现。当数据到了尾部如何处理,它将转回到0位置来处理。这个的转回是通过取模操作来执行的
因此环列队列的是逻辑上将数组元素q[0]与q[MAXN-1]连接,形成一个存放队列的环形空间,为了方便读写,还要用数组下标来指明队列的读写位置

1. head: head指向可以读的位置
2. tail: tail指向可以写的位置 

环形队列的关键是判断队列为空,还是为满

1. 当tail追上head时,队列为写满
2. 当head追上tail时,队列为读空

如何判断环形队列为空,为满有两种判断方法

1. 附加一个标志位tag
    1) 当head赶上tail,队列空,则tag = 0
    2) 当tail赶上head,队列满,则tag = 1

2. 限制tail赶上head,即队尾结点与队首结点之间至少留有一个元素的空间
    1) 队列空: head == tail
    2) 队列满: (tail+1) % MAXN == head

Relevant Link:

http://docs.linuxtone.org/ebooks/C&CPP/c/ch12s05.html
http://coolshell.cn/tag/disruptor
http://www.searchtb.com/2012/10/introduction_to_disruptor.html
http://www.cnblogs.com/haolujun/archive/2012/10/03/2710726.html

3. 环形队列编程实现

0x1: 附加标志环形队列Ring Buffer

ringq.h

#ifndef __RINGQ_H__
#define __RINGQ_H__

#ifdef __cplusplus
extern "C"
{
#endif 

#define QUEUE_MAX 20

/*
1. 初始化状态: q->head = q->tail = q->tag = 0;
2. 队列为空:(q->head == q->tail) && (q->tag == 0)
3. 队列为满: ((q->head == q->tail) && (q->tag == 1))

1. 入队操作: 如队列不满,则写入: q->tail =  (q->tail + 1) % q->size ;
2. 出队操作:如果队列不空,则从head处读出
3. 下一个可读的位置: q->head =  (q->head + 1) % q->size
*/
typedef struct ringq
{
   int head; /* 头部,出队列方向*/
   int tail; /* 尾部,入队列方向*/
   int tag ; /* 为空还是为满的标志位*/
    int size ; /* 队列总尺寸 */
   int space[QUEUE_MAX]; /* 队列空间 */
}RINGQ;

/*
第一种设计方法:
当head == tail 时,tag = 0 为空,等于 = 1 为满
*/

extern int ringq_init(RINGQ * p_queue);

extern int ringq_free(RINGQ * p_queue);

/* 加入数据到队列 */
extern int ringq_push(RINGQ * p_queue,int data);

/* 从队列取数据 */
extern int ringq_poll(RINGQ * p_queue,int *p_data);

#define ringq_is_empty(q) ( (q->head == q->tail) && (q->tag == 0))

#define ringq_is_full(q) ( (q->head == q->tail) && (q->tag == 1))

#define print_ringq(q) printf("ring head %d,tail %d,tag %d\n", q->head,q->tail,q->tag);
#ifdef __cplusplus
}
#endif 

#endif /* __RINGQ_H__ */

ringq.c

#include <stdio.h>
#include "ringq.h"

int ringq_init(RINGQ * p_queue)
{
    p_queue->size = QUEUE_MAX ;

    p_queue->head = 0;
    p_queue->tail = 0;

    p_queue->tag = 0;

    return 0;
}

int ringq_free(RINGQ * p_queue)
{
    return 0;
}

int ringq_push(RINGQ * p_queue,int data)
{
    print_ringq(p_queue);

    if(ringq_is_full(p_queue))
    {
        printf("ringq is full\n");
        return -1;
    }

    p_queue->space[p_queue->tail] = data;

    p_queue->tail = (p_queue->tail + 1) % p_queue->size ;

    /* 这个时候一定队列满了*/
    if(p_queue->tail == p_queue->head)
    {
        p_queue->tag = 1;
    }

    return p_queue->tag ;
}

int ringq_poll(RINGQ * p_queue,int * p_data)
{
    print_ringq(p_queue);
    if(ringq_is_empty(p_queue))
    {
        printf("ringq is empty\n");
        return -1;
    }

    *p_data = p_queue->space[p_queue->head];

    p_queue->head = (p_queue->head + 1) % p_queue->size ;

    /* 这个时候一定队列空了*/
    if(p_queue->tail == p_queue->head)
    {
        p_queue->tag = 0;
    }
    return p_queue->tag ;
}

/* 测试第一种环形队列*/
void test5()
{
    RINGQ rq, * p_queue;
    int i,data;

    p_queue = &rq;

    ringq_init(p_queue);

    for(i=0; i < QUEUE_MAX +2 ; i++)
    {
        ringq_push(p_queue,i+1);
    } 

    if(ringq_poll(p_queue,&data)>=0)
    PRINT_INT(data);

    if(ringq_poll(p_queue,&data)>=0)
    PRINT_INT(data);

    if(ringq_poll(p_queue,&data)>=0)
    PRINT_INT(data);

    if(ringq_poll(p_queue,&data)>=0)
    PRINT_INT(data);

    if(ringq_poll(p_queue,&data)>=0)
    PRINT_INT(data);

    if(ringq_poll(p_queue,&data)>=0)
    PRINT_INT(data);

    ringq_free(p_queue);
}

/* 测试第一种环形队列,更加复杂的情况*/
void test6()
{
    RINGQ rq, * p_queue;
    int i,data;

    p_queue = &rq;

    ringq_init(p_queue);
    ringq_push(p_queue,1);
    ringq_push(p_queue,2); 

    if(ringq_poll(p_queue,&data)>=0)
    PRINT_INT(data);

    if(ringq_poll(p_queue,&data)>=0)
    PRINT_INT(data);

    if(ringq_poll(p_queue,&data)>=0)
    PRINT_INT(data);

    if(ringq_poll(p_queue,&data)>=0)
    PRINT_INT(data);

    ringq_push(p_queue,3);
    ringq_push(p_queue,4);
    ringq_push(p_queue,5); 

    if(ringq_poll(p_queue,&data)>=0)
    PRINT_INT(data);

    if(ringq_poll(p_queue,&data)>=0)
    PRINT_INT(data);

    ringq_push(p_queue,6); 

    if(ringq_poll(p_queue,&data)>=0)
    PRINT_INT(data);

    if(ringq_poll(p_queue,&data)>=0)
    PRINT_INT(data);

    ringq_free(p_queue);
} 

int mani()
{
    test5();
    test6();

    return 0;
}

0x2: 预留空间环形队列

ringq.h

#ifndef __RINGQ_H__
#define __RINGQ_H__

#ifdef __cplusplus
extern "C"
{
#endif 

#define RINGQ_MAX 20

/*
1. 初始化状态: q->head = q->tail = q->tag = 0;
2. 队列为空:(q->head == q->tail)
3. 队列为满: (((q->tail+1)%q->size) == q->head )

1. 入队操作: 如队列不满,则写入: q->tail =  (q->tail + 1) % q->size ;
2. 出队操作:如果队列不空,则从head处读出
3. 下一个可读的位置: q->head = (q->head + 1) % q->size
*/
typedef struct ringq
{
   int head; /* 头部,出队列方向*/
   int tail; /* 尾部,入队列方向*/
   int size ; /* 队列总尺寸 */
   int space[RINGQ_MAX]; /* 队列空间 */
}RINGQ;

/*
  取消tag .限制读与写之间至少要留一个空间
  队列空 head == tail .
  队列满是 (tail+1)%MAX == head
  初始化是head = tail = 0;
*/

extern int ringq_init(RINGQ * p_ringq);

extern int ringq_free(RINGQ * p_ringq);

extern int ringq_push(RINGQ * p_ringq,int data);

extern int ringq_poll(RINGQ * p_ringq,int * p_data);

#define ringq_is_empty(q) (q->head == q->tail)

#define ringq_is_full(q) (((q->tail+1)%q->size) == q->head )

#define print_ringq2(q,d) printf("ring head %d,tail %d,data %d\n", q->head,q->tail,d);

#ifdef __cplusplus
}
#endif 

#endif /* __QUEUE_H__ */

ringq.c

#include <stdio.h>
#include "ringq.h"

int ringq_init(RINGQ * p_ringq)
{
    p_ringq->size = RINGQ_MAX;

    p_ringq->head = 0;
    p_ringq->tail = 0;

    return p_ringq->size;
}

int ringq_free(RINGQ * p_ringq)
{
    return 0;
}

/* 往队列加入数据 */
int ringq_push(RINGQ * p_ringq,int data)
{
    print_ringq(p_ringq,data);

    if(ringq_is_full(p_ringq))
    {
        printf("ringq is full,data %d\n",data);
        return -1;
    }

    p_ringq->space[p_ringq->tail] = data;
    p_ringq->tail = (p_ringq->tail + 1) % p_ringq->size ;   

    return p_ringq->tail ;
}

int ringq_poll(RINGQ * p_ringq,int * p_data)
{
    print_ringq(p_ringq,-1);
    if(ringq_is_empty(p_ringq))
    {
        printf("ringq is empty\n");
        return -1;
    }

    *p_data = p_ringq->space[p_ringq->head];
    p_ringq->head = (p_ringq->head + 1) % p_ringq->size ;

    return p_ringq->head;
}

Relevant Link:

http://blog.csdn.net/sking002007/article/details/6584590
http://blog.chinaunix.net/uid-26669601-id-3737307.html
http://blog.sina.com.cn/s/blog_79c57c3d01019v8p.html

Copyright (c) 2015 LittleHann All rights reserved

时间: 2024-10-10 07:00:44

Circular Queue Implementation Principle的相关文章

[LeetCode] Design Circular Queue 设计环形队列

Design your implementation of the circular queue. The circular queue is a linear data structure in which the operations are performed based on FIFO (First In First Out) principle and the last position is connected back to the first position to make a

LeetCode 622. Design Circular Queue

原题链接在这里:https://leetcode.com/problems/design-circular-queue/ 题目: Design your implementation of the circular queue. The circular queue is a linear data structure in which the operations are performed based on FIFO (First In First Out) principle and th

循环队列(Circular Queue)

循环队列(Circular Queue) 1. 循环队列的概念 1.1 循环队列的定义 为了能够充分地使用数组中的存储空间,克服"假溢出"现象,可以把数组的前端和后端连接起来,形成一个环形的表,即把存储队列元素的表从逻辑上看成一个环,成为循环队列(circular queue). 1.2 循环队列中各元素的逻辑及存储关系 循环队列的首尾相接,当队头指针front和队尾指针rear进到maxSize-1后,再前进一个位置就自动到0.这可以利用除法取余的运算(%)来实现. (1)队头指针进

Jan 12 - Implement Queue using Stacks; Stack; Queue Implementation;

代码: class MyQueue { // Push element x to the back of queue. Stack<Integer> stack = new Stack<>(); Stack<Integer> aux = new Stack<>(); public void push(int x) { while(!stack.isEmpty()){ aux.push(stack.pop()); } stack.push(x); while(

Circular Queue--循环队列

队列大家见得很多了,形式也比较简单,就是一个特化的链表,它的enqueue.dequeue操作相当于链表的addLast.removeFirst操作.关于链表的实现,可以查看我的另一篇博文--"LinkedList--链表".下面仅讨论一个稍微复杂点的情况--循环队列. 循环队列的就是用循环数组来实现队列,有一个问题需要解决:在普通的非循环队列中,rear == front 说明队列为空队列,然而在循环队列中,这个可能意味着队列为空,也可能意味着队列为满.通常采用两种策略来处理这个问题

循环队列Circular Queue

循环队列:先进先出,从头出:front+1,从尾进:rear+1,空判断:front==rear,满判断(rear+1)%maxsize==front //循环队列的实现 //定义队列结构体 define MAXSIZE 100 typedef struct{ int *base; //存储内存分配基地址 int front; //队列头索引 int rear; //队列尾索引 }circularQueue; //队列初始化void InitQueue(circularQueue *q){ q-

LeetCode 641. Design Circular Deque

原题链接在这里:https://leetcode.com/problems/design-circular-deque/ 题目: Design your implementation of the circular double-ended queue (deque). Your implementation should support following operations: MyCircularDeque(k): Constructor, set the size of the dequ

Invalidation queue with "bit-sliceability"

BACKGROUND, FEATURES In a computer system having more than one memory storage facility, a special data integrity challenge can occur. Any computer system having both a main-memory structure and cache-memory(s) is in such a situation (e.g. see System

MPSC lock free queue

[c实现的队列](http://www.1024cores.net/home/lock-free-algorithms/queues/non-intrusive-mpsc-node-based-queue) 下面是akka实现的一个MPSC队列. PS: 代码中注释对链头链尾判定的标准是添加的元素所在的位置为链尾,这和代码中的命名相冲突了 PPS: single customer 就不太需要考虑消费者的同时取的竞争状态 /**  * Copyright (C) 2009-2014 Typesaf