1. 程式人生 > >3.5.2 迴圈佇列——佇列的順序表示和實現

3.5.2 迴圈佇列——佇列的順序表示和實現

佇列也有兩種表示形式,順序和鏈式。 與順序棧相類似,在佇列的順序儲存結構中,除了用一組地址連續的儲存單元依次存放從佇列頭到佇列尾的元素之外,尚需附設兩個整形變數front和rear分別指示佇列頭元素及佇列尾元素的位置(後面分別稱為頭指標和尾指標)。佇列的順序儲存結構表示如下
//--------佇列的順序儲存結構------------
#define MAXQSIZE 100      //佇列可能達到的最大長度
typedef struct
{
    QElemType *base;     //儲存空間的基地址
    int front;           //頭指標[通過基地址用陣列進行訪問]
    int rear;            //尾指標
}SqQueue;
為了在c語言中表述方便在此約定初始化建立空佇列時,令front=rear=0,每當插入新的佇列尾元素時,尾指標rear增1;每當刪除佇列的頭元素時,頭指標front增1。因此在非空佇列中,頭指標始終指向佇列的頭元素,而尾指標始終指向佇列尾元素的下一個位置。
假設當前佇列分配的最大空間是6,則當佇列處於圖3.12(d)所示的狀態時不可再繼續插入新的隊尾元素,否則會出現溢位現象,也就是因陣列越界而導致程式的非法操作錯誤。事實上此時佇列可用空間並沒有佔滿,所以這種現象成為“假溢位”。這是由“隊尾入隊,隊頭出隊”這種受限制的操作造成的。 解決這種“假溢位”的一個方法是將順序棧變成迴圈佇列如圖3.13。 頭、尾指標以及佇列的元素關係不變,只是在迴圈佇列中,頭、尾指標“依迴圈增1”的操作可用“模”運算來實現。通過取模,頭指標和尾指標就可以在順序表空間內以頭尾銜接的方式“迴圈”移動。  
在圖3.14(a)中,隊頭元素是J5,在元素J6入隊之前,在Q.rear的值為5,當元素J6入隊之後,通過“模”運算,Q.rear=(Q.rear+1)%6,得到的Q.rear的值為0,而不會出現圖3.12(d)的“假溢位”狀態。 在圖3.14(b)中,j7,j8,j9,j10相繼入隊,則佇列空間均被佔滿,此時頭尾指標相同。 在圖3.14(c)中,若j5和j6相繼從圖3.14(a)所示的佇列中出隊,使佇列此時呈“空”的狀態,頭尾指標的值也是相同的。 所以,迴圈佇列不能以頭尾指標是否相同作為是否隊“滿”的判斷依據。 所以有以下兩種方法可以斷定是否隊滿。 (1)少一個元素空間,即佇列空間大小為m時,有m-1個元素認為是隊滿。這樣判斷隊空的條件不變,即當頭、尾指標的值相同時,則認為隊空,而當尾指標在迴圈意義上加1後是等於頭指標的,則認為隊滿,因此,在迴圈佇列中隊空和隊滿的條件是: 隊空判斷條件:Q.rear==Q.front 隊滿判斷條件:(Q.rear+1)%6==Q.front 如圖3.14(d),當j7,j8,j9進入圖3.14(a)所示的佇列後,(Q.rear+1)%MAXQSIZE的值等於Q.front,此時認為隊滿。 (2)另設一個標誌位以區別佇列是“空”還是“滿”。具體描述參考【資料結構】嚴蔚敏 C語言 第二版 第三章習題演算法設計題的第(7)題,由讀者自行設計完成。 下面給出第一種方案的實現過程 1.初始化 迴圈佇列的初始化操作就是動態分配一個預定義大小為MAXQSIZE的陣列空間。 演算法3.11 迴圈佇列的初始化 【演算法步驟】 ①為佇列分配一個最大容量位MAXQSIZE的陣列空間,base為執行陣列空間的首地址 ②頭指標和尾指標置為零,表示佇列為空 【演算法描述】
Status InitQueue(SqQueue &Q)
{
Q.
base=new QElemType[MAXQSIZE]; //分配一個QElemType型別的大小為MAXQSIZE大小的陣列空間 if(!Q.base) exit(OVERFLOW); //記憶體分配失敗 Q.front=Q.rear=0; //頭指標和尾指標相等且置零,佇列置空 return OK; }
2.求佇列長度 對於非迴圈佇列,尾指標和頭指標的差值就是佇列的長度,但是對於迴圈佇列,差值可能為負數,所以應該將差值加上MAXQSIZE,然後再與MAXQSIZE求餘。 演算法3.12 求迴圈佇列的長度 【演算法描述】
int QueueLength(SqQueue Q)
{//返回Q的元素個數
return (Q.rear-Q.front+MAXQSIZE)%MAXQSIZE;
}
3入隊 【演算法步驟】 ①判斷是否隊滿,若滿返回ERROR ②將新元素加入隊尾 ③隊尾指標加1 【演算法描述】
Status EnQueue(SqQueue &Q,QElemType e)
{//插入元素e到佇列中
if((Q.rear+1)%MAXQSIZE==Q.front) return ERROR;//如果隊滿則返回ERROR
Q.base[Q.rear]=e;//把新元素插入佇列
Q.rear=(Q.rear+1)%MAXQSIZE;//隊尾指標加1
return OK;
}
4出隊 出隊操作就是將隊頭元素取出並刪掉 【演算法步驟】 ①判斷是否隊空,空就返回ERROR ②取出隊頭元素並儲存 ③隊頭元素加1 【演算法描述】
Status DeQueue(SqQueue &Q,QElemTyoe &e)
{
if(Q.rear==Q.front) return ERROR;
e=Q.base[Q.front];
Q.front=(Q.front+1)%MAXQSIZE;
return OK;
}
5、取隊頭元素 【演算法步驟】 ①判斷是否隊空,如果空就返回ERROR ②返回隊頭元素 【演算法描述】
SElemType GetHead(SqQueue Q)
{//
if((Q.rear+1)%MAXQSIZE==Q.front) return ERROR;
return Q.base[Q.front];
}

由上述分析可見,如果使用者的應用程式中設有迴圈佇列,則必須為它設定一個佇列長度;若使用者無法估計所用佇列的最大長度,則易採用鏈隊。