循环队列队空与队满两个状态的判断算法分析


循环队列队空与队满两个状态的判断算法分析

线性表是数据结构中比较重要的一种逻辑结构,插入删除操作是线性表的基本操作,
当进行这些操作时,不仅需要考虑插入、删除的位置是否合法性,
仍然需要考虑‘满’与‘空’这两种状态,但是,由于栈和队列都是受限制的线性表,
它们已经规定了进行插入、删除的位置,所以插入、删除时不需要再考虑位置的合法性,
只需要考虑‘满’与‘空’。
1 队列与栈的区别
数据结构中,队列是只允许在一端进行删除(队头)另一端进行插入(队尾)的线性表;
栈是只允许在一端进行插入删除(栈顶)的线性表。由于两者都是线性表,
则遵循线性表的存储结构表示—顺序存储以及链式存储,
这里主要讨论静态分配的顺序存储结构。如下:
#define MAXSIZE 100 //初始分配空间
typedef struct {
Elemtype data[MAXSIZE];//一维数组
int Top; //栈顶指针,一直指向栈顶元素,初始值为-1
}sqStack;
typedef struct {
Elemtype data[MAXSIZE];//一维数组
int front; //队头指针指向队头元素,初始值为0
int rear; //队尾指针指向队尾元素的下一个位置,初始值为0
}sqQueue;
2 顺序队列与栈判断满和空的不同
线性表顺序存储的静态分配特点是初始化时一次性分配好所需内存空间(MAXSIZE),
因此在插入删除时需要判断‘空’和‘满’两个状态。由于栈的所有操作只在栈顶进行,
所以只通过栈顶值Top就可以反映出当前存储空间使用的情况,当Top==-1时栈空,
Top==MAXSIZE-1时栈满。顺序队列的操作分别在队头队尾两端进行,在出队入队时,
对头front和队尾rear值都是只增加(向MAXSIZE靠近)而不减小,
如果仅通过rear==MAXSIZE来判断顺序队列是否满,
此时可能存在rear已经指向MAXSIZE同时front>0(因为出队使front值增加)
而不能做入队操作的情况,导致元素出队后的空闲存储空间永远无法重新利用,
尽管这时循环队列中实际的元素个数远远的小于最大存储空间MAXSIZE,
这就造成了顺序队列中的“假上溢”现象。
以上进一步看出栈与顺序队列在进行插入删除时空与满的判断条件不一样。
顺序队列的“假上溢”现象:
为克服“假上溢”现象,可以将顺序队列想象为一个首尾相接的环状空间,称为循环队列。
在循环队列中出队入队时,头尾指针仍要向前移动进行加1操作,
当头尾指针指向上界MAXSIZE时,
头尾指针加1操作的结果重新指向下界0(加1后对MAXSIZE做MOD取余数运算)。
循环队列是解决了“假上溢”现象,但对循环队列进行插入删除时如何判断队空与队满?
3 循环队列队空与队满的判断
循环队列入队时尾指针向前追赶头指针,出队时头指针向前追赶尾指针,
故存在队空和队满时都有front==rear的情况,
因此无法通过front==rear来判断队空还是队满, 下面就给出五种算法来解决循环队列空和满的问题。
算法一、增加两个全局变量A,B,分别表示所有元素入队的次数A和元素出队的次数B。
初始化A=0,B=0;
出队时,当front==rear&&A==B(即入队元素次数等于出队元素次数)时队空,
不能出队,然后B++;
入队时,当front==rear&&(A-B==MAXSIZE)
(表示入队元素数多于出队元素数,但两者差必须为MAXSIZE)时队满,不能入队,然后A++。
算法二、增加一个计数变量Count,表示队列中实际存在的数据元素的个数。
初始化Count=0;
出队时,当Count==0队空,不能出队,然后Count--;
入队时,当Count==MAXSIZE队满,不能入队,然后Count++。
算法三、增加一个布尔变量Tag来区分。
初始化 Tag=0,并且入队成功时置Tag=1、出队成功时置Tag=0;
出队是队头在追赶队尾,如果追上了,即rear==front&&tag==0表示队空,
不能出队,然后重新置Tag=0;
入队是队尾追赶队头(沿着空位置向队头方向移动),
如果追上了,即rear==front&&tag==1时表示队满,不能入队,然后重新置Tag=1。
算法四、约定牺牲存储空间中的一个存储单元来区分。(教材中大多采用这种算法)。
出队时,当rear==front队空,不能出队;
入队时,当(rear+1)%MAXSIZE==front队满,不能入队。
算法五、将静态分配的循环队列改为动态分配的循环队列。
以上可见,循环队列不能使用动态分配的一维数组实现,但是循环队列也是顺序队列,
遵循顺序存储结构的分配方式,即可以静态分配存储空间,
也可以根据队列的长度动态分配存储空间。如下:顺序队列在动态分配顺序存储结构:
#define QUESIZE 100 //初始分配量
#define QUEUEINCREMENT 10 //动态分配增量
typedef struct{
ElemType *elem; //动态分配存储空间基址
int front;
int rear;
int quesize; //当前分配的存储容量(以sizeof(ElemType)为单位)
}sqQueue;
入队时(rear+1)%quesize==front判断是否队满,
满则动态再分配QUEUEINCREMENT的空间,重新构成新的循环队列,
新队列头尾指针要分不同情况来修改。因为原队列队满时,
头尾指针有不同的分布情况,所以先分析出不同分布后再分别根据新增加空间来修改头尾指针。
出队时rear==front队空,不能出队;
4 总结
基于以上五种算法循环队列在实现入队出队操作时就比较容易了,
不过一定要注意算法实现顺序,入队时先判断是不是队满,
出队时先判断是不是队空,然后再进行相应的入队出队操作。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值