一、什么是順序隊列?
隊列的順序儲存結構:用數組存儲隊列,為了避免當只有一個元素時,隊頭和隊尾重合使得處理變得麻煩,所以引入兩個指針:front 指針指向隊頭元素,rear 指針指向隊尾元素的下一個位置,當 front=rear 時,為空隊列,結構如下圖所示:
順序隊列的結構代碼如下:
typedef int ElemType; /* ElemType類型根據實際情況而定,這里假設為int */
/* 循環隊列的順序存儲結構 */
typedef struct
{
ElemType data[MAXSIZE];
int front; /* 頭指針 */
int rear; /* 尾指針,若隊列不空,指向隊列尾元素的下一個位置 */
}SeqQueue;
假設是長度為 5 的數組,初始狀態,空隊列如下圖左所示,front 與 rear 指針均指向下標為 0 的位置。然后入隊 a1、a2、a3、a4,front 指針依然指向下標為 0 位置,而 rear 指針指向下標為 4 的位置,如下圖右所示:
出隊 a1、a2,則 front 指針指向下標為 2 的位置,rear 不變,如下圖左所示,再入隊 a5,此時 front 指針不變,rear 指針移動到數組之外。數組之外, 那將是哪里?如下圖右所示:
問題還不止於此。假設這個隊列的總個數不超過5個,但目前如果接着入隊的話,因數組末尾元素已經占用,再向后加,就會產生數組越界的錯誤,可實際上,我們的隊列在下標為0和1的地方還是空閑的。我們把這種現象叫做 “假溢出"。
二、什么是順序循環隊列?
所以解決 "假溢出" 的辦法就是后面滿了,就再從頭開始,也就是頭尾相接的循環。我們把隊列的這種頭尾相接的順序存儲結構稱為順序循環隊列。
剛才的例子繼續,上圖的 rear 可以改為指向下標為 0 的位置,這樣就不會造成指針指向不明的問題了,如下圖所示:
接着入隊 a6,將它放置於下標為 0 處,rear 指針指向下標為 1 處,如下圖左所示。若再入隊 a7,則 rear 指針就與 front 指針重合,同時指向下標為 2 的位置,如下圖右所示:
此時問題又出來了,我們剛才說,空隊列時,front 等於 rear,現在當隊列滿時,也是 front 等於 rear,那么如何判斷此時的隊列究竟是空還是滿呢?
- 辦法一是設置一個標志變量 flag, 當 front == rear,且 flag = 0 時為隊列空,當 front== rear,且 flag= 1 時為隊列滿。
- 辦法二是當隊列空時,條件就是 front = rear,當隊列滿時,我們修改其條件,保留一個元素空間。也就是說,隊列滿時,數組中還有一個空閑單,例如下圖所示,我們就認為此隊列已經滿了,也就是說,我們不允許上圖右的情況出現:
我們重點來討論第二種方法,由於 rear 可能比 front 大,也可能比 front 小,所以盡管它們只相差一個位置時就是滿的情況,但也可能是相差整整一圈。 所以若隊列的最大尺寸為 QueueSize,那么隊列滿的條件是(rear+1) % QueueSize == front
,因此通用的計算隊列長度公式為:(rear - front + QueueSize) % QueueSize
。
注意:front 指針和 rear 指針后移不能直接使用 ++,而要使用Q->front = (Q->front + 1) % MAXSIZE
,因為到達數組尾后需要移動到數組開頭。
三、基本操作
3.1 初始化隊列操作
實現代碼如下:
// 初始化隊列操作
Status initQueue(SeqQueue *Q)
{
Q->front = 0;
Q->rear = 0;
return TRUE;
}
3.2 入隊操作
實現代碼如下:
// 入隊操作
Status enQueue(SeqQueue *Q, const ElemType e)
{
// 判斷隊列是否已滿
if ((Q->rear + 1) % MAXSIZE == Q->front)
return FALSE;
Q->data[Q->rear] = e; // 將元素e賦值給隊尾
Q->rear = (Q->rear + 1) % MAXSIZE; // rear指針向后移一位置,若到最后則轉到數組頭部
return TRUE;
}
3.3 出隊操作
實現代碼如下:
// 出隊操作
Status deQueue(SeqQueue *Q, ElemType *e)
{
// 判斷是否為空隊
if (Q->front == Q->rear)
return FALSE;
*e = Q->data[Q->front]; // 將隊頭元素賦值給e
Q->front = (Q->front + 1) % MAXSIZE; // front指針向后移一位置,若到最后則轉到數組頭部
return TRUE;
}
3.3 遍歷隊列操作
實現代碼如下:
// 遍歷隊列操作
Status tarverseQueue(const SeqQueue Q)
{
int cur = Q.front; // 當前指針
while (cur != Q.rear) // 直到cur指向了隊尾元素的下一個位置,即Q.rear,結束循環
{
printf("%d ", Q.data[cur]);
cur = (cur + 1) % MAXSIZE; // 當前指針向后推移
}
printf("\n");
return TRUE;
}
四、完整程序
#include <stdio.h>
#include <stdlib.h>
#define TRUE 1
#define FALSE 0
#define MAXSIZE 5 /* 存儲空間初始分配量 */
typedef int Status;
typedef int ElemType; /* ElemType類型根據實際情況而定,這里假設為int */
/* 順序循環隊列的順序存儲結構 */
typedef struct
{
ElemType data[MAXSIZE];
int front; // 頭指針
int rear; // 尾指針,若隊列不空,指向隊列尾元素的下一個位置
}SeqQueue;
Status initQueue(SeqQueue *Q); // 初始化隊列操作
Status enQueue(SeqQueue *Q, const ElemType e); // 入隊操作
Status deQueue(SeqQueue *Q, ElemType *e); // 出隊操作
Status tarverseQueue(const SeqQueue Q); // 遍歷隊列操作
Status clearQueue(SeqQueue *Q); // 清空隊列操作
Status isEmpty(const SeqQueue Q); // 判斷是否為空隊列
Status getHead(const SeqQueue Q, ElemType *e); // 獲得隊頭元素
int getLength(const SeqQueue Q); // 獲得隊列的長度
// 初始化隊列操作
Status initQueue(SeqQueue *Q)
{
Q->front = 0;
Q->rear = 0;
return TRUE;
}
// 入隊操作
Status enQueue(SeqQueue *Q, const ElemType e)
{
// 判斷隊列是否已滿
if ((Q->rear + 1) % MAXSIZE == Q->front)
return FALSE;
Q->data[Q->rear] = e; // 將元素e賦值給隊尾
Q->rear = (Q->rear + 1) % MAXSIZE; // rear指針向后移一位置,若到最后則轉到數組頭部
return TRUE;
}
// 出隊操作
Status deQueue(SeqQueue *Q, ElemType *e)
{
// 判斷是否為空隊
if (Q->front == Q->rear)
return FALSE;
*e = Q->data[Q->front]; // 將隊頭元素賦值給e
Q->front = (Q->front + 1) % MAXSIZE; // front指針向后移一位置,若到最后則轉到數組頭部
return TRUE;
}
// 遍歷隊列操作
Status tarverseQueue(const SeqQueue Q)
{
int cur = Q.front; // 當前指針
while (cur != Q.rear) // 直到cur指向了隊尾元素的下一個位置,即Q.rear,結束循環
{
printf("%d ", Q.data[cur]);
cur = (cur + 1) % MAXSIZE; // 當前指針向后推移
}
printf("\n");
return TRUE;
}
// 清空隊列操作
Status clearQueue(SeqQueue *Q)
{
Q->front = Q->rear = 0;
return TRUE;
}
// 判斷是否為空隊列
Status isEmpty(const SeqQueue Q)
{
return Q.front == Q.rear ? TRUE : FALSE;
}
// 獲得隊頭元素
Status getHead(const SeqQueue Q, ElemType *e)
{
if (Q.front == Q.rear) // 判斷是否為空隊列
return FALSE;
*e = Q.data[Q.front];
return TRUE;
}
// 獲得隊列的長度
int getLength(const SeqQueue Q)
{
return (Q.rear - Q.front + MAXSIZE) % MAXSIZE;
}
int main()
{
SeqQueue Q;
// 初始化隊列
initQueue(&Q);
// 入隊操作
for (int i = 0; i < MAXSIZE - 1; i++)
enQueue(&Q, i);
printf("入隊操作(0、1、2、3)! \n\n");
// 出隊操作
ElemType d;
deQueue(&Q, &d);
printf("刪除的元素是%d \n\n", d);
// 遍歷隊列
printf("遍歷隊列: ");
tarverseQueue(Q);
printf("\n");
// 判斷是否為空隊列
printf("現在隊列空否? %u (1:空 0:否)\n\n", isEmpty(Q));
// 獲得隊列的長度
printf("隊列長度為: %d \n\n", getLength(Q));
// 獲得隊頭元素
getHead(Q, &d);
printf("隊頭元素是%d \n\n", d);
return 0;
}
輸出結果如下圖所示:
參考:
《大話數據結構 - 第4章》 棧與隊列