3.3 队列—队列的存储实现及运算实现
与线性表、栈类似,队列也有顺序存储和链式存储两种存储方法。
1、顺序队
顺序存储的队称为顺序队。因为队的队头和队尾都是活动的,因此,除了队列的数据区外还有队头、队尾两个指针。顺序队的类型定义如下:
1 #define MAXSIZE 1024 /*队列的最大容量*/ 2 typedef struct 3 { 4 datatype data[MAXSIZE]; /*队员的存储空间*/ 5 int rear,front; /*队头队尾指针*/ 6 }SeQueue;
定义一个指向队的指针变量:
申请一个顺序队的存储空间:
队列的数据区为:
队头指针:sq->front
队尾指针:sq->rear
设队头指针指向队头元素前面一个位置,队尾指针指向队尾元素(这样的设置是为了某些运算的方便,并不是唯一的方法)。
置空队则为:sq->front=sq->rear=-1;
在不考虑溢出的情况下,入队操作队尾指针加1,指向新位置后,元素入队。操作如下:
1 sq->rear++; 2 sq->data[sq->rear]=x; /*原队头元素送x 中*/
在不考虑队空的情况下,出队操作队头指针加1,表明队头元素出队。操作如下:
1 sq->front++; 2 x=sq->data[sq->front];
队中元素的个数:m=(sq->rear)-(q->front);
队满时:m= MAXSIZE; 队空时:m=0。
按照上述思想建立的空队及入队出队示意图如图3.12 所示,设MAXSIZE=10。从图中可以看到,随着入队出队的进行,会使整个队列整体向后移动,这样就出现了图3.12(d)中的现象:队尾指针已经移到了最后,再有元素入队就会出现溢出,而事实上此时队中并未真的“满员”,这种现象为“假溢出”,这是由于“队尾入队头出”这种受限制
的操作所造成。解决假溢出的方法之一是将队列的数据区data[0..MAXSIZE-1]看成头尾相接的循环结构,头尾指针的关系不变,将其称为“循环队”,“循环队”的示意图如图3.13所示。
因为是头尾相接的循环结构,入队时的队尾指针加1 操作修改为:
1 sq->rear=(sq->rear+1) % MAXSIZE;
出队时的队头指针加1 操作修改为:
1 sq->front=(sq->front+1) % MAXSIZE;
设MAXSIZE=10,图3.14 是循环队列操作示意图。
从图3.14 所示的循环队可以看出,(a)中具有a5 、a6 、a7 、a8 四个元素,此时front=4,rear=8;随着a9~a14 相继入队,队中具有了10 个元素---队满,此时front=4,rear=4,如(b)所示,可见在队满情况下有:front==rear。若在(a)情况下,a5~a8 相继出队,此时队空, front=8,rear=8,如(c)所示,即在队空情况下也有:front==rear。就是说“队满”和“队空”的条件是相同的了。这显然是必须要解决的一个问题。
方法之一是附设一个存储队中元素个数的变量如num,当num==0 时队空,当num==MAXSIZE 时为队满。
另一种方法是少用一个元素空间,把图(d)所示的情况就视为队满,此时的状态是队尾指针加1 就会从后面赶上队头指针,这种情况下队满的条件是: (rear+1) %MAXSIZE==front,也能和空队区别开。
下面的循环队列及操作按第一种方法实现。循环队列的类型定义及基本运算如下:
1 typedef struct 2 { 3 datatype data[MAXSIZE]; /*数据的存储区*/ 4 int front,rear; /*队头队尾指针*/ 5 int num; /*队中元素的个数*/ 6 }c_SeQueue; /*循环队*/
⑴ 置空队
1 c_SeQueue* Init_SeQueue() 2 { 3 q=malloc(sizeof(c_SeQueue)); 4 q->front=q->rear=MAXSIZE-1; 5 q->num=0; 6 return q; 7 }
⑵ 入队
1 int In_SeQueue ( c_SeQueue *q , datatype x) 2 { 3 if (num==MAXSIZE) 4 { 5 printf("队满"); 6 return –1; /*队满不能入队*/ 7 } 8 else 9 { 10 q->rear=(q->rear+1) % MAXSIZE; 11 q->data[q->rear]=x; 12 num++; 13 return 0; /*入队完成*/ 14 } 15 }
⑶ 出队
1 int Out_SeQueue (c_SeQueue *q , datatype *x) 2 { 3 if (num==0) 4 { 5 printf("队空"); 6 return –1; /*队空不能出队*/ 7 } 8 else 9 { 10 q->front=(q->front+1) % MAXSIZE; 11 *x=q->data[q->front]; /*读出队头元素*/ 12 num--; 13 return 0; /*出队完成*/ 14 } 15 }
⑷ 判队空
1 int Empty_SeQueue(c_SeQueue *q) 2 { 3 if (num==0) 4 return 0; 5 else 6 return -1; 7 }
2. 链队
链式存储的队称为链队。和链栈类似,用单链表来实现链队,根据队的FIFO 原则,为了操作上的方便,我们分别需要一个头指针和尾指针,如图3.15 所示。
图3.15 中头指针front 和尾指针rear 是两个独立的指针变量,从结构性上考虑,通常将二者封装在一个结构中。链队的描述如下:
1 typedef struct node 2 { 3 datatype data; 4 struct node *next; 5 } QNode; /*链队结点的类型*/ 6 typedef struct 7 { 8 QNode *front,*rear; 9 }LQueue; /*将头尾指针封装在一起的链队*/
定义一个指向链队的指针:
1 LQueue *q;
按这种思想建立的带头结点的链队如图3.16 所示。
链队的基本运算如下:
(1) 创建一个带头结点的空队:
1 LQueue *Init_LQueue() 2 { 3 LQueue *q; 4 QNode *p; 5 q=malloc(sizeof(LQueue)); /*申请头尾指针结点*/ 6 p=malloc(sizeof(QNode)); /*申请链队头结点*/ 7 p->next=NULL; 8 q->front=q->rear=p; 9 return q; 10 }
(2) 入队
1 void In_LQueue(LQueue *q , datatype x) 2 { 3 QNode *p; 4 p=malloc(sizeof(QNnode)); /*申请新结点*/ 5 p->data=x; 6 p->next=NULL; 7 q->rear->next=p; 8 q->rear=p; 9 }
(3) 判队空
1 int Empty_LQueue( LQueue *q) 2 { 3 if (q->front==q->rear) 4 return 0; 5 else 6 return -1; 7 }
(4) 出队
1 int Out_LQueue(LQueue *q , datatype *x) 2 { 3 QNode *p; 4 if (Empty_LQueue(q) ) 5 { 6 printf ("队空"); 7 return -1; 8 } /*队空,出队失败*/ 9 else 10 { 11 p=q->front->next; 12 q->front->next=p->next; 13 *x=p->data;/*队头元素放x 中*/ 14 free(p); 15 if (q->front->next==NULL) 16 q->rear=q->front; 17 /*只有一个元素时,出队后队空,此时还要修改队尾指针,参考图3.16(c)*/ 18 return 0; 19 } 20 }
posted on 2015-05-14 14:47 chunlanse2014 阅读(661) 评论(0) 编辑 收藏 举报