【筆記】順序佇列
一、佇列的定義
和棧相反,佇列說一種先進先出(縮寫為FIFO)的線性表。它只允許在表的一端進行插入,在另一端除元素。在佇列中,允許插入的一端叫隊尾,允許刪除的一端則成為隊頭。
還有一種限定性的資料結構說雙端佇列。雙端佇列是限定插入和刪除操作中表的兩端進行的線性表。
佇列的常用操作包括:
- 初始化佇列:建立一個佇列。
- 入隊:將一個元素新增到隊尾。
- 出隊:將一隊頭的元素去除,同時刪除該元素,使下一個元素稱為隊頭。
- 獲取佇列的第1個元素:將隊頭的元素取出,不刪除該元素(隊頭仍然是該元素)。
- 獲取佇列長度:根據隊頭和隊尾計算出佇列中元素的數量。
二、佇列的順序儲存表示
1.順序佇列
順序佇列通常採用一維陣列一依次存放從隊頭到隊尾的元素。同時,使用兩個指標分別指示陣列中存放的某一個元素和最後一個元素的位置。其中指向第一個元素的指標被稱為隊頭指標front,指向最後一個元素的指標被稱為隊尾指標rear。
在使用佇列前,先初始化佇列,此時佇列為空,隊頭指標front和隊尾指標rear都指向佇列的第一個位置,即front=rear=0。
每一個元素進入佇列,隊尾指標rear會增1。
每一個元素出佇列時,隊頭指標front會增1。
假溢位:在對順序佇列進行插入和刪除操作的過程中,可能會出現“假溢位”現象。經過多次插入和刪除操作後,實際上佇列還有儲存空間,但是又無法向佇列中插入元素,我們將這種溢位稱為“假溢位”。
2.順序迴圈佇列
為了充分利用儲存空間,消除這種“假溢位”現象,當隊尾指標rear和隊頭指標front到達儲存空間的最大值的時候,讓隊尾指標和隊頭指標轉化為0,這樣就可以把元素插入到佇列還沒有利用的儲存單元中。這樣就可以把順序佇列使用的儲存空間構造成一個邏輯上首尾相連的迴圈佇列,可以通過取餘操作實現佇列的邏輯上的首尾相連。
初始化建空佇列時,令front=rear=0,每當插入新的佇列元素時,尾指標增1;每當刪除佇列頭元素時,頭指標增。因此,在非空佇列中,頭指標始終指向佇列頭元素,而尾指標時鐘指向佇列尾元素的下一個位置。
僅憑等式Q.front=Q.rear無法判別佇列空間時空還是滿。可有兩種處理方法:其一時另設一個標誌位以區別佇列時空還是滿;其二是少用一個元素空間,約定“佇列頭指標在佇列尾指標的下一位置上”作為佇列呈“滿”狀態的標誌。
如果使用者的應用程式中設有迴圈佇列,則必須為它設定一個最大佇列長度;若使用者無法預估所用的佇列的最大長度,則宜採用鏈佇列。
常用的確定隊首位置,隊尾位置,佇列長度的語句:
Q.front=(Q.front+1)%MAXQSIZE;
Q.rear=(Q.rear+1)%MAXQSIZE;
(Q.rear-Q.front+MAXQSIZE)%MAXQSIZE;
三、佇列的順序儲存實現
1.順序佇列的實現
- 型別定義
#define QueueSize 12
typedef struct Squeue{ /*順序佇列型別定義*/
DataType queue[QueueSize];
int front,rear; /*隊頭指標和隊尾指標*/
}SeqQueue;
- 初始化佇列
void InitQueue(SeqQueue *SQ)
/*將順序佇列初始化為空佇列只需要把隊頭指標和隊尾指標同時置為0*/
{
SQ->front=SQ->rear=0; /*把隊頭指標和隊尾指標同時置為0*/
}
- 佇列狀態
int QueueEmpty(SeqQueue SQ)
/*判斷佇列是否為空,佇列為空返回1,否則返回0*/
{
if(SQ.front==SQ.rear) /*判斷隊頭指標和隊尾指標是否相等*/
return 1; /*當佇列為空時,返回1;否則返回0*/
else
return 0;
}
- 入隊函式
int EnQueue(SeqQueue *SQ,DataType x)
/*將元素x插入到順序佇列SQ中,插入成功返回1,否則返回0*/
{
if(SQ->rear==QueueSize) /*在插入新的元素之前,判斷隊尾指標是否到達陣列的最大值,即是否佇列已滿*/
return 0;
SQ->queue[SQ->rear]=x; /*在隊尾插入元素x */
SQ->rear=SQ->rear+1; /*隊尾指標向後移動一個位置*/
return 1;
}
- 出隊函式
int DeQueue(SeqQueue *SQ,DataType *e)
/*刪除順序佇列中的隊頭元素,並將該元素賦值給e,刪除成功返回1,否則返回0*/
{
if(SQ->front==SQ->rear) /*在刪除元素之前,判斷佇列是否為空*/
return 0;
else
{
*e=SQ->queue[SQ->front]; /*將要刪除的元素賦值給e*/
SQ->front=SQ->front+1; /*將隊頭指標向後移動一個位置,指向新的隊頭*/
return 1;
}
}
- 取隊頭元素
int GetHead (SeqQueue SQ,DataType *e)
/*取隊頭元素,並將該元素賦值給e,取元素成功返回1,否則返回0*/
{
if(SQ.front==SQ.rear) /*在取隊頭元素之前,判斷順序迴圈佇列是否為空*/
return 0;
else
{
*e=SCQ.queue[SQ.front]; /*將隊頭元素賦值給e,取出隊頭元素*/
return 1;
}
}
- 清空佇列
int ClearQueue(SeqQueue *SQ)
/*清空佇列*/
{
SQ->front=SQ->rear=0; /*將隊頭指標和隊尾指標都置為0*/
}
- 列印佇列
void PrintQueue(SeqQueue SQ)
{
int cur;
DataType e;
printf("當前佇列為:\n");
cur=SQ.front;
while(cur!=SQ.rear)
{
e=SQ.queue[cur++]; /*將隊頭元素賦值給e,取出隊頭元素*/
printf("%c ",e);
}
printf("\n");
}
- 主程式
void main()
{
int i,a;
DataType p[12],e;
SeqQueue Q;
InitQueue(&Q);
while(1)
{
printf("請選擇操作:1.入隊 2.出隊 3.退出\n");
scanf("%d",&a);
getchar();
switch(a)
{
case 1:
if(Q.rear<QueueSize)
{
printf("請輸入入隊元素:\n");
scanf("%c",&e);
EnQueue(&Q,e);
PrintQueue(Q);
}
else
{
printf("佇列已滿!\n");
}
break;
case 2:
if(!QueueEmpty(Q))
{
DeQueue(&Q,&e);
printf("出隊元素是:%c\n",e);
PrintQueue(Q);
}
break;
case 3:
default:
printf("程式結束!");
exit(1);
}
}
}
- 測試結果
2.順序迴圈佇列的實現
- 型別定義
#define QueueSize 12
typedef struct Squeue{ /*順序佇列型別定義*/
DataType queue[QueueSize];
int front,rear; /*隊頭指標和隊尾指標*/
}SeqQueue;
- 初始化佇列
void InitQueue(SeqQueue *SQ)
/*將順序佇列初始化為空佇列只需要把隊頭指標和隊尾指標同時置為0*/
{
SQ->front=SQ->rear=0; /*把隊頭指標和隊尾指標同時置為0*/
}
- 佇列狀態
int QueueEmpty(SeqQueue SQ)
/*判斷佇列是否為空,佇列為空返回1,否則返回0*/
{
if(SQ.front==SQ.rear) /*判斷隊頭指標和隊尾指標是否相等*/
return 1; /*當佇列為空時,返回1;否則返回0*/
else
return 0;
}
- 入隊函式
int EnQueue(SeqQueue *SQ,DataType e)
/*將元素x插入到順序佇列SQ中,插入成功返回1,否則返回0*/
{
if(SQ->front==(SQ->rear+1)%QueueSize) /*在插入新的元素之前,判斷隊尾指標是否到達陣列的最大值,即是否佇列已滿*/
return 0;
SQ->queue[SQ->rear]=e; /*在隊尾插入元素x */
SQ->rear=(SQ->rear+1)%QueueSize; /*隊尾指標向後移動一個位置*/
return 1;
}
- 出隊函式
int DeQueue(SeqQueue *SQ,DataType *e)
/*刪除順序佇列中的隊頭元素,並將該元素賦值給e,刪除成功返回1,否則返回0*/
{
if(SQ->front==SQ->rear) /*在刪除元素之前,判斷佇列是否為空*/
return 0;
else
{
*e=SQ->queue[SQ->front]; /*將要刪除的元素賦值給e*/
SQ->front=(SQ->front+1)%QueueSize; /*將隊頭指標向後移動一個位置,指向新的隊頭*/
return 1;
}
}
- 取隊頭元素
int GetHead (SeqQueue SCQ,DataType *e)
/*取隊頭元素,並將該元素賦值給e,取元素成功返回1,否則返回0*/
{
if(SCQ.front==SCQ.rear) /*在取隊頭元素之前,判斷順序迴圈佇列是否為空*/
return 0;
else
{
*e=SCQ.queue[SCQ.front]; /*將隊頭元素賦值給e,取出隊頭元素*/
return 1;
}
}
- 清空佇列
int ClearQueue(SeqQueue *SCQ)
/*清空佇列*/
{
SCQ->front=SCQ->rear=0; /*將隊頭指標和隊尾指標都置為0*/
}
- 顯示佇列
void DisplayQueue(SeqQueue SQ)
/*順序迴圈佇列的顯示輸出函式。首先判斷佇列是否為空,輸出時還應考慮隊頭指標和隊尾指標值的大小問題*/
{
int i;
if(QueueEmpty(SQ)) /*判斷順序迴圈佇列是否為空*/
return 0;
if(SQ.front<SQ.rear)
/*如果隊頭指標值小於隊尾指標的值,則把隊頭指標到隊尾指標指向的元素依次輸出*/
for(i=SQ.front;i<SQ.rear;i++)
printf("%c ",SQ.queue[i]);
else
/*如果隊頭指標值大於隊尾指標的值,則把隊尾指標到隊頭指標指向的元素依次輸出*/
for(i=SQ.front;i<SQ.rear+QueueSize;i++)
printf("%c ",SQ.queue[i%QueueSize]);
printf("\n");
}
- 主程式
void main()
{
SeqQueue Q; /*定義一個順序迴圈佇列*/
char e; /*定義一個字元型別變數,用於存放出佇列的元素*/
InitQueue(&Q); /*初始化順序迴圈佇列*/
/*將3個元素A,B,C依次進入順序迴圈佇列*/
printf("A入隊\n");
EnQueue(&Q,'A');
printf("B入隊\n");
EnQueue(&Q,'B');
printf("C入隊\n");
EnQueue(&Q,'C');
/*將順序迴圈佇列中的元素顯示輸出*/
printf("佇列中元素:");
DisplayQueue(Q);
/*將順序迴圈佇列中的隊頭元素出佇列*/
printf("隊頭元素第一次出隊\n");
DeQueue(&Q,&e);
printf("出隊的元素:");
printf("%c\n",e);
printf("隊頭元素第二次出隊\n");
DeQueue(&Q,&e);
printf("出隊的元素:");
printf("%c\n",e);
/*將順序迴圈佇列中的元素顯示輸出*/
printf("佇列中元素:");
DisplayQueue(Q);
/*將3個元素D,E,F依次進入順序迴圈佇列*/
printf("D入隊\n");
EnQueue(&Q,'D');
printf("E入隊\n");
EnQueue(&Q,'E');
printf("F入隊\n");
EnQueue(&Q,'F');
/*將順序迴圈佇列中的元素顯示輸出*/
printf("佇列中元素:");
DisplayQueue(Q);
}
- 測試結果