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

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

第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 。 下图给出了循环队列的几种情况。

【循环队列判空判满问题】与一般的非空顺序队列相同,在非空循环队列中,队头指针顺序队列的基本操作示意图 front rear front rear front rear front rear (a ) 空队列

(b ) a ,b,c,d 入队 (c ) a,b,c 出队

(d ) e,f 入队

始终指向当前的队头元素,而队尾指针始终指向真正队尾元素后面的单元。在下图 (c)所示循环队列中,队列头元素

是e 3,队列尾元素是e 5,当e 6、e 7和e 8相继入队后,队列空间均被占满,如上图 (b)所示,此时队尾指针追上队头指针,所以有:front =rear 。反之,若e 3、e 4和e 5相继从上图 (c)的队列中删除,则得到空队列,如上图 (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);

(a)空队列

(b) 队列满

(c) 一般情况 循环队列示意图

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、队列的定义与特点

队列 (Queue) 是另一种限定性的线性表,它只允许在表的一端插入元素,而在另一端删除元素。队列具有先进先出 (Fist In Fist Out,缩写为FIFO)的特性。

2、队列的基本操作

进队(插入)、出队(删除)。

3、队列的存储:链队列、循环队列

4、循环队列:将顺序队列数组视为首尾相接的队列,模运算支持克服假溢出。

区分循环队列队空队满的两种方法:

◆损失一个元素空间;

◆增设标志量tag。

相关文档
最新文档