隊列
隊列是一種特殊的線性表,特殊之處在於它只允許在表的前端(front)進行刪除操作,而在表的后端(rear)進行插入操作,和棧一樣,隊列是一種操作受限制的線性表。進行插入操作的端稱為隊尾,進行刪除操作的端稱為隊頭。
一個隊列為z=(a1,a2,...,an), 如圖
那么a1為對頭元素,an為隊尾元素。最早進入隊列的元素也會最早出來,只有當最先進入隊列的元素都出來以后,后進入的元素才能退出。
在日常生活中,人們去銀行辦理業務需要排隊,這就類似我們提到的隊列。每一個新來辦理業務的需要按照機器自動生成的編號等待辦理,只有前面的人辦理完畢,才能輪到排在后面的人辦理業務。新來的人進入排隊狀態就相當於入隊,前面辦理完業務離開的就相當於出隊。
隊列有兩種存儲表示:順序存儲和鏈式存儲。采用順序存儲結構的隊列被稱為順序隊列,采用鏈式存儲結構的隊列稱為鏈式隊列。
基本運算
InitQueue() ——初始化隊列
EnQueue() ——進隊列
DeQueue() ——出隊列
IsQueueEmpty() ——判斷隊列是否為空
IsQueueFull() ——判斷隊列是否已滿
順序隊列
由於順序隊列的底層使用的是數組,因此需預先申請一塊足夠大的內存空間初始化順序隊列。除此之外,為了滿足順序隊列中數據從隊尾進,隊頭出且先進先出的要求,我們還需要定義兩個指針(top 和 rear)分別用於指向順序隊列中的隊頭元素和隊尾元素。
隊列為空時,隊頭指針front和隊尾指針rear都指向下標為0的存儲單元,當元素a,b,c,d,e,f,g依次進入隊列后,元素ag分別存放在數組下標為06的存儲單元中,隊頭指針front指向元素a,隊尾指針指rear向元素g的下一位置。如圖所示。
假溢出
在順序隊中,當尾指針已經到了數組的上界,不能再有入隊操作,但其實數組中還有空位置,這就叫“假溢出”。解決假溢出的途徑———采用循環隊列。
例如在圖中隊列刪除a和b,然后依次插入h、i和j,當插入j后,就會出現隊尾指針rear越出數組的下界造成“假溢出”,如圖
順序循環隊列
為充分利用向量空間,克服"假溢出"現象的方法是:將向量空間想象為一個首尾相接的圓環,並稱這種向量為循環向量。存儲在其中的隊列稱為循環隊列(Circular Queue)。即:循環隊列中進行出隊、入隊操作時,頭尾指針仍要加1,朝前移動。只不過當頭尾指針指向向量上界(QueueSize-1)時,其加1操作的結果是指向向量的下界0。
隊空和隊滿
在循環隊列中,隊空和隊滿時隊頭front和隊尾指針rear同時都會指向同一存儲單元,即front==rear,如圖所示。
隊空
隊滿
如何區分隊空和隊滿呢?有以下兩種方法:
(1)增加一個標志位。設標志位為tag,初始時,tag=0;當入隊成功,則tag=1;出隊成功,tag=0。則判斷隊空的條件為:frontrear&&tag0;隊滿的條件為:frontrear&&tag1;
(2)少用一個存儲單元。隊空的判斷條件為frontrear;隊滿的判斷條件為front(rear+1)%QueueSize。
隊滿的狀態如圖。
存儲結構
#define MAXQSIZE 5 // 存儲空間的初始分配量
typedef struct {
ElemType *base;
int front;
int rear;
int maxSize;
} SqQueue;
基本運算
初始化
Status InitQueue(SqQueue &Q) {
//分配存儲空間
Q.base = (ElemType*)malloc(MAXQSIZE * sizeof(ElemType));
if(!Q.base) exit(OVERFLOW);
//置Q為空隊列
Q.front = Q.rear = 0;
Q.maxSize = MAXQSIZE;
return OK;
}
判隊列是否為空
Status QueueEmpty(SqQueue Q) {
if(Q.rear == Q.front) return TRUE;
else return FALSE;
}
入隊函數
Status EnQueue(SqQueue &Q, ElemType e) {
if((Q.rear + 1) % MAXQSIZE == Q.front)//隊列已滿
return ERROR;
Q.base[Q.rear] = e;//插入隊尾
Q.rear = (Q.rear + 1) % MAXQSIZE;//尾部指針后移,如果到最后則轉到頭部
return OK;
}
出隊函數
Status DeQueue(SqQueue &Q, ElemType &e) {
if(Q.front == Q.rear) //隊列空
return ERROR;
//返回隊頭元素
e = Q.base[Q.front];
//隊頭指針后移,如到最后轉到頭部
Q.front = (Q.front + 1) % MAXQSIZE;
return OK;
}
輸出循環隊列函數
void OutQueue(SqQueue Q) {
ElemType e;
if(QueueEmpty(Q)){
printf("這是一個空隊列!");
} else {
while(!QueueEmpty(Q)){
DeQueue(Q, e);
printf("%6d", e);
}
printf("\n");
}
}
輸出循環隊列長度
Status QueueLength(SqQueue Q) {
return (Q.rear - Q.front + MAXQSIZE) % MAXQSIZE;
}
銷毀隊列
Status ClearQueue(SqQueue Q) {
///銷毀隊列Q,Q不再存在
if(Q.base)
free(Q.base);
Q.base = NULL;
Q.front = Q.rear = 0;
return OK;
}
主函數
int main() {
SqQueue q;
int cord;
ElemType a;
printf("第一次使用必須初始化!\n");
//調用初始化算法
InitQueue(q);
do{
printf("\n 主菜單 \n");
printf(" 1 初始化循環隊列 ");
printf(" 2 進隊一個元素 ");
printf(" 3 出隊一個元素 ");
printf(" 4 隊列長度 ");
printf(" 5 銷毀隊列 ");
printf(" 6 結束程序運行 ");
printf("\n------------------------------------------------------------------\n");
printf("請輸入您的選擇( 1, 2, 3, 4, 5, 6)");
scanf("%d", &cord);
printf("\n");
switch(cord) {
case 1:
InitQueue(q); //調用初始化算法;
OutQueue(q);
break;
case 2:
printf("請輸入要插入的數據元素:a=");
scanf("%d", &a);
EnQueue (q, a); //調用進隊算法;
printf("%d 進隊之后的隊列:",a);
OutQueue(q);
break;
case 3:
DeQueue (q, a); //調用出隊算法;
printf("隊頭元素 %d 出隊之后的隊列:", a);
OutQueue(q);
break;
case 4:
printf("該隊列長度為: %d", QueueLength(q));
break;
case 5:
ClearQueue(q);
break;
case 6:
exit(0);
}
} while(cord <= 4);
return 0;
}