1. 程式人生 > 實用技巧 >9.順序佇列、迴圈佇列、鏈佇列

9.順序佇列、迴圈佇列、鏈佇列

/*
4.11 佇列的抽象資料型別
ADT 佇列(Queue)
Data
    同線性表。元素具有相同腐乳型別,相鄰元素具有前驅和後繼關係。
operation
    InitQueue(*Q)  //初始化操作,建立一個空佇列Q。
    DestoryQueue(*Q)   //若佇列Q存在,則銷燬它。
    ClearQueue(*Q)     //將佇列Q清空。
    QueueEmpty(Q)      //若佇列Q為空,返回true,否則返回false。
    GetHead(Q, *e)     //若佇列Q存在且非空,用e返回佇列Q的隊頭元素。
    EnQueue(*Q, e)      //若佇列Q存在,插入新元素e到佇列Q中併成為隊尾元素。
    DeQueue(*Q, *e)     //刪除佇列Q中隊頭元素,並用e返回其值。
    QueueLength(Q)      //返回佇列Q的元素個數
endADT
*/ /* 4.12 迴圈佇列 線性表有順序儲存和鏈式儲存,棧是線性表,所以有這兩種儲存方式。同樣,佇列作為一種特殊的線性表, 也同樣存在這兩種儲存方式。 */ /* 4.12.1 佇列順序儲存結構的不足 和順序線性儲存結構一致,定長,會溢位。 隊頭為下標0的位置,隊尾為下標為1的位置,出隊時會移動元素,時間複雜度為O(n) 4.12.2 迴圈佇列 迴圈佇列通過移動隊頭指標的位置,解決出隊元素前移時間複雜度為O(n);通過將隊尾指標迴圈,與隊頭指標 實現閉合操作,充分利用佇列資料位;但是會存在資料溢位問題。 所以我們直接來看佇列的鏈式儲存結構吧 4.13 佇列的鏈式儲存結構及實現 佇列的鏈式儲存結構,其實就是線性表的單鏈表,只不過它只能尾進頭出而已,我們把它簡稱位鏈佇列。
*/ //鏈佇列的結構為: /*QElemType型別根據實際情況而定,這裡假設為int*/ typedef int QElemType; //結點結構 typedef struct QNode { QElemType data; struct QNode *next; } QNode, *QueuePtr; //佇列的連結串列結構 typedef struct { //隊頭、隊尾指標 QueuePtr front,rear; } LinkQueue; //4.13.1佇列的鏈式儲存結構--入隊操作 //插入元素e為Q的新的隊尾元素,需要結合配圖才能理解 Status EnQueue(LinkQueue *Q, QElemType e) { QueuePtr s
=(QueuePtr)malloc(sizeof(QNode)); //儲存分配失敗 if (!s) exit(OVERFLOW); s->data = e; s->next = NULL; //把擁有元素e新節點s賦值給原隊尾結點的後繼,Q->rear->next = s; Q->rear->next = s; //把當前的s設定為隊尾結點,rear指向s Q->rear = s; return OK; } //4.13.2 佇列的鏈式儲存結構--出棧操作 //若佇列不空,刪除Q的隊頭元素,用e返回其值,並返回OK,否則返回ERROR Status DeQueue(LinkQueue *Q, QElemType *e) { QueuePtr p; if (Q->front == Q->rear) return ERROR; //將欲刪除的隊頭結點暫存給p p = Q->front->next; //將欲刪除的隊頭結點的值賦給e *e = p->data; //將原隊頭結點後繼p->next賦值給頭節點後繼 Q->front->next = p->next; //若隊頭是隊尾。則刪除後將rear指向頭結點 if(Q->rear == p) Q->rear = Q->front; free(p); return OK; } /* 對於迴圈佇列與鏈佇列的比較,可以從兩方面來考慮,從時間上,其實它們的基本操作都是常數時間,即都為O(1), 不過迴圈佇列是事先申請好空間,使用期間不釋放,而對於鏈佇列,每次申請和釋放結點也會存在一些時間開銷,如果入隊 和出隊頻繁,則兩者還是有細微差異。對於空間上來說,迴圈佇列必須有一個固定的長度,所以就有了儲存元素個數和空間 浪費的問題。而鏈佇列不存在這個問題,儘管它需要一個指標域,會產生一些空間上的開銷,但也可以接受,所以在空間上, 鏈佇列更加靈活。 總的來說,在可以確定佇列長度最大值的情況下,建議用迴圈佇列,如果你無法預估佇列的長度時,則用鏈佇列。 */