3.9队列的顺序存储(循环队列)

3.9队列的顺序存储(循环队列)

1、顺序队列的假溢出现象

队列的一种顺序存储称为顺序队列

与顺序栈类似,在队列的顺序存储结构中,用一组地址连续的存储单元依次存放从队头到队尾的元素,如一维数组 Queue[MAXSIZE]。

由于队列中队头和队尾的位置都是动态变化的,因此需要附设两个指针 front 和 rear。

  • front:指示队头元素在数组中的位置;
  • rear:指示真实队尾元素相邻的下一个位置。

初始化队列时,令 front = rear =0;

入队时,直接将新元素送入尾指针 rear 所指的单元, 然后尾指针增 1;

出队时,直接取出队头指针 front 所指的元素,然后头指针增 1。

显然,在非空顺序队列中,队头指针始终指向当前的队头元素,而队尾指针始终指向真正队尾元素后面的单元。当 rear==MAXSIZE 时,认为队满。但此时不一定是真的队满,因为随着部分元素的出队,数组前面会出现一些空单元,如下图(d)所示。由于只能在队尾入队,使得上述 空单元无法使用。把这种现象称为假溢出,真正队满的条件是 rear - front=MAXSIZE

在这里插入图片描述

2. 循环队列

为了解决假溢出现象并使得队列空间得到充分利用,一个较巧妙的办法是将顺序队列的数组看成一个环状的空间,即规定最后一个单元的后继为第一个单元,我们形象地称之为循环队列

假设队列数组为 Queue[MAXSIZE],当 rear+1=MAXSIZE 时,令 rear=0,即可求得最后一个单元 Queue[MAXSIZE-1]的后继:Queue[0]。

更简便的办法是通过数学中的取模(求余)运算来实现:rear=(rear+1)mod MAXSIZE,显然,当 rear+1=MAXSIZE 时,rear=0, 同样可求得最后一个单元 Queue[MAXSIZE-1]的后继:Queue[0]。

所以,借助于取模(求余) 运算,可以自动实现队尾指针、队头指针的循环变化。
进队操作时,队尾指针的变化是:rear= (rear+1)mod MAXSIZE ;
而出队操作时,队头指针的变化是:front=(front+1)mod MAXSIZE。

下图给出了循环队列的几种情况:

循环队列判空判满问题

与一般的非空顺序队列相同,在非空循环队列中,队头指针始终指向当前的队头元素,而队尾指针始终指向真正队尾元素后面的单元。
在这里插入图片描述
队列头元素是 e3,队列尾元素是 e5,当 e6、e7和 e8相继入队后,队列空间均被占满,如上图 (b)所示, 此时队尾指针追上队头指针,所以有:front =rear。

反之,若 e3、e4 和 e5相继从上图 ©的 队列中删除,则得到空队列,如上图 (a)所示,此时队头指针追上队尾指针,所以也存在关 系式:front = rear。可见,只凭 front = rear 无法判别队列的状态是“空”还是“满”。

对于这个问题,可有两种处理方法

  • 一种方法是少用一个元素空间。当队尾指针所指向的空单元 的后继单元是队头元素所在的单元时,则停止入队。这样一来,队尾指针永远追不上队头指 针,所以队满时不会有 front =rear。现在队列“满”的条件为(rear+1)mod MAXSIZE=front。 判队空的条件不变,仍为 rear=front。
  • 另一种是增设一个标志量的方法,以区别队列是“空” 还是“满”。主要介绍损失一个存储空间以区分队列空与满的方法。

循环队列的类型定义

#define MAXSIZE 50  /*队列的最大长度*/ typedef struct 
{  
	QueueElementType  element[MAXSIZE];  /* 队列的元素空间*/  
	int  front;  /*头指针指示器*/  
	int  rear ;  /*尾指针指示器*/ 
}SeqQueue;

循环队列的基本操作

(1) 初始化操作
void InitQueue(SeqQueue * Q) 
{  /* 将*Q 初始化为一个空的循环队列 */ 
	Q->front=Q->rear=0; 
} 
(2) 入队操作
int EnterQueue(SeqQueue *Q, QueueElementType x) 
{  /*将元素 x 入队*/ 
	if((Q->rear+1)%MAXSIZE==Q->front)  /*队列已经满了*/ 
  		return(FALSE); 
  	Q->element[Q->rear]=x; 
	Q->rear=(Q->rear+1)%MAXSIZE;  /* 重新设置队尾指针 */ 
	return(TRUE);  /*操作成功*/ 
} 
(3)出队操作
int DeleteQueue(SeqQueue *Q, QueueElementType * x) 
{ /*删除队列的队头元素,用 x 返回其值*/ 
	if(Q->front==Q->rear)  /*队列为空*/ 
    	return(FALSE); 
   	*x=Q->element[Q->front]; 
   	Q->front=(Q->front+1)%MAXSIZE;  /*重新设置队头指针*/ 
  	return(TRUE);  /*操作成功*/ 
} 

这里采用了第一种处理假溢出问题的方法。如果采用第二种方法,则需要设置一个标志量 tag。

初始化操作即产生一个空的循环队列,此时 Q->front = Q->rear=0,tag=0;当空 的循环队列中有第一个元素入队时,则 tag=1,表示循环队列非空;当 tag=1 且 Q->front=Q->rear 时,表示队满。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值