您的位置:首页 > 其它

[循环队列]使用原因与注意事项

2015-07-11 00:24 309 查看
问题描述:循环队列是一般队列的变种吧,就是将队列首尾相连了,貌似这样就不必考虑队列满而无法使用了,因为到了队列尾又会循环回到队列首。在嵌入式底层代码实现中,一些串行端口数据特别是串口,用到循环队列的情况还是蛮多的。当然,这只是一种数据结构,用在哪里都得看具体用途和是否能带来好处。为了更深一步的对这一结构的了解,进行了下整理和学习。
循环队列:[/b]将向量空间想象为一个首尾相接的圆环,并称这种向量为循环向量。存储在其中的队列称为循环队列(Circular
Queue)。这个应当是比较成熟的定义了,简洁了当。像是一条蛇咬住自己的尾巴,而且蛇腔里面可以填充东西的感觉。

为什么用循环队列:主要是为了克服“假溢出”情况,系统作为队列用的存储区还没有满,但队列却发生了溢出,我们把这种现象称为"假溢出"。其实就是因为队列的first
in first out特质所引起的,队列为获得first
in first out特质,一般需要两个指针,一个称为“头指针”,一个称为“尾指针”,头指针用于在队列头部读出元素,尾指针用于在队列尾部插入新元素,先进去队列的元素位于队列的头部。对于使用顺序队列情况,随着队列尾部不断插入新元素,尾指针最终会指向分配给队列的最后的内存地址,当再有新元素要插入时,此时队列尾部已经无法插入新元素了。而头指针由于有元素出队列,队列内存空间的前面一部分其实还是空的,因此就造成了“假溢出”这种情况。循环队列就是为解决该问题的。(当然,解决假溢出还可以将队列元素做平移,但感觉使用循环队列会有更高的效率)。

循环队列弊端:循环队列空和满时都是头指针等于尾指针,因此对于队列空和满的判断需要在代码中加以区别,有三种方式:一是另设一布尔变量来区别队列的空和满。二是少用一个元素的空间,每次入队前测试入队后头尾指针是否会重合,如果会重合就认为队列已满。三是设置一计数器记录队列中元素总数,不仅可判别空或满,还可以得到队列中元素的个数。

循环队列类型定义:

#define QueueSize 100 //应根据具体情况定义该值

typedef char DataType; //DataType的类型依赖于具体的应用

typedef struct{
int
front; //头指针,队非空时指向队头元素
int
rear; //尾指针,队非空时指向队尾元素的下一位置
int
count;//计数器,记录队中元素总数

DataType data[QueueSize];

}CirQueue;


循环队列的基本运算:


1. 队列置空:

void InitQueue(CirQueue *Q)
{
Q->front=Q->rear=0;
Q->count=0; //计数器置0
}

2. 队列判空:

int QueueEmpty(CirQueue *Q)
{
return Q->count==0; //队列无元素为空
}

3. 判断队列是否满:

int QueueFull(CirQueue *Q)
{
return Q->count==QueueSize; //队中元素个数等于QueueSize时队满
}

4. 元素队列尾部入队:

void EnQueue(CirQueue *Q,DataType x)
{
if(QueueFull(Q))
Error("Queue overflow"); //队满上溢
Q->count ++; //队列元素个数加1
Q->data[Q->rear]=x; //新元素插入队尾
Q->rear=(Q->rear+1)%QueueSize; //循环意义下将尾指针加1
}

5. 元素从队列头部出队列:

DataType DeQueue(CirQueue *Q)
{
DataType temp;
if(QueueEmpty(Q))
Error("Queue underflow"); //队空下溢
temp=Q->data[Q->front];
Q->count--; //队列元素个数减1
Q->front=(Q->front+1)%QueueSize; //循环意义下的头指针加1
return temp;
}

循环队列使用总结:优点是逻辑判断简单,执行程序实现容易。
缺点是执行时重复比较太多,效率不一定高,大型系统节点较多时不宜采用(重复比较太多的问题暂时未碰到,队列用来传递FIFO消息的情况较多,重复比较问题以后碰到再补充)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: