迴圈佇列與鏈佇列的簡單實現
阿新 • • 發佈:2018-12-30
一、迴圈佇列
a、概念
為充分利用向量空間,克服"假溢位"現象的方法是:將向量空間想象為一個首尾相接的圓環,並稱這種向量為迴圈向量。儲存在其中的佇列稱為迴圈佇列(Circular Queue)。
通過上圖可以看出,如果使用順序表作為佇列的話,當處於d狀態則不能繼續插入新的隊尾元素,否則會因為陣列越界而導致程式程式碼被破壞。
所以產生了由連結串列實現的迴圈佇列,只有佇列未滿時才可以插入新的隊尾元素。判斷迴圈佇列是空佇列還是滿佇列依據:頭尾指標相同就是空,尾指標的下一個是頭指標就是滿。
b、程式碼及執行結果
#include <stdio.h> #include <stdlib.h> #define OK 1 #define ERROR 0 #define MAXQSIZE 5 typedef int Status; typedef int QElemType; typedef struct Node { QElemType *base; //初始化動態分配儲存空間 int front; int rear; } SqQueue; Status InitQueue(SqQueue *Q) { Q->base = (QElemType *)malloc(MAXQSIZE * sizeof(QElemType)); if (!Q->base) return ERROR; Q->front = Q->rear = 0; return OK; } Status EnQueue(SqQueue *Q, QElemType elem) { //佇列為空時 1%5==1,佇列滿時(4+1)%5==0,最多容納4個元素 if ((Q->rear + 1) % MAXQSIZE == (Q->front)) return ERROR; Q->base[Q->rear] = elem; Q->rear = (Q->rear + 1) % MAXQSIZE; //rear始終在0-5中迴圈 return OK; } Status OutQueue(SqQueue *Q, QElemType *e) { if (Q->front == Q->rear) return ERROR; *e = Q->base[Q->front]; Q->front = (Q->front + 1) % MAXQSIZE; return OK; } Status PrintQueue(SqQueue Q) { printf("the queue is:"); for (int i = Q.front; i < Q.rear; ++i) printf("%d ", Q.base[i]); return OK; } int main() { SqQueue queue; QElemType elem; int i; while(!InitQueue(&queue)); EnQueue(&queue,1); EnQueue(&queue,2); EnQueue(&queue,3); EnQueue(&queue,4); EnQueue(&queue,5);//佇列已滿,無效入隊 printf("\noutput:"); scanf("%d", &i); while(i != 0) { OutQueue(&queue, &elem); i--; } PrintQueue(queue); return OK; }
二、鏈佇列
a、概念
佇列的鏈式儲存結構,其實就是線性表的單鏈表,只不過它只能尾進頭出而已,我們把它簡稱為鏈佇列。
當佇列為空時,front和rear都指向頭結點;
入隊:在佇列尾部插入結點;
出隊:頭結點的後繼結點(隊頭)出隊,將頭結點的後繼改為他後面的結點,若連結串列除頭結點外只剩一個元素時,則需將rear指向頭結點。
一般情況下,鏈佇列的出隊圖示:
b、程式碼及執行結果
#include<stdio.h> #include<stdlib.h> //鏈佇列結點結構 typedef struct QNode{ int data; struct QNode *next; }QNode; //鏈佇列結構 typedef struct LiQueue{ QNode *front; QNode *rear; }LiQueue; //建立鏈佇列 LiQueue initQueue(){ LiQueue *lq=(LiQueue *)malloc(sizeof(LiQueue)); if(lq ==NULL){ return *lq; } lq->front=lq->rear=NULL; return *lq; } //判斷鏈佇列是否為空 int isEmpty(LiQueue *lq){ if(lq->front==NULL || lq->rear==NULL){ return 0; }else{ return 1; } } //元素進鏈佇列 void enQueue(LiQueue *lq,int x){ QNode *p; p=(QNode *)malloc(sizeof(QNode)); p->data=x; p->next=NULL; if(lq->rear==NULL){ lq->front=lq->rear=p;//如果第一次插入則設定頭指標和尾指標為p }else{ lq->rear->next=p;//鏈佇列的尾部插入p lq->rear=p;//設定鏈佇列的尾指標指向p } } //元素出鏈佇列 int deQueue(LiQueue *lq,int *y){ QNode *p=lq->front; if(lq->rear==NULL || lq->front==NULL){ return 0; } if(lq->front==lq->rear){ lq->front=lq->rear=NULL; }else{ lq->front=lq->front->next; } *y=p->data; free(p); return 1; } //列印鏈佇列 void printQueue(LiQueue lq){ if(lq.front==NULL || lq.rear==NULL){ return; } while(lq.front!=NULL){ printf("%d\n",lq.front->data); lq.front=lq.front->next; } } int main(void){ int y=0; LiQueue lq=initQueue(); enQueue(&lq,1); enQueue(&lq,2); enQueue(&lq,3); enQueue(&lq,4); enQueue(&lq,5); //deQueue(&lq,&y); printQueue(lq); printf("出佇列元素=%d\n",y); return 0; }
三、迴圈佇列和鏈佇列的比較
從時間上,其實它們的基本操作都是常數時間,即都為0(1)的,不過迴圈佇列是事先申請好空間,使用期間不釋放,而對於鏈佇列,每次申請和釋放結點也會存在一些時間開銷,如果入隊出隊頻繁,則兩者還是有細微差異。
對於空間上來說,迴圈佇列必須有一個固定的長度,所以就有了儲存元素個數和空間浪費的問題。而鏈佇列不存在這個問題,儘管他需要一個指標域,會產生一些空間上的開銷,但也可以接受。所以在空間上,鏈佇列更加靈活。
總的來說,在可以確定佇列長度的情況下,建議用迴圈佇列;不能預估佇列長度時,建議用鏈佇列。