隊列的定義
隊列(queue)是隻允許在一端進行插入操作,另一端進行刪除操作的線性表
隊列是一種先進先出的線性表,允許插入的一端稱爲隊尾(rear),允許刪除的一端稱爲隊頭(front)。向隊列中插入元素稱爲入隊,從隊列中刪除元素稱爲出隊。當隊列中沒有元素時稱爲空隊列。隊列的操作是按先進先出的原則進行的,即新添加的元素總是加到隊尾,每次離開的元素總是隊頭的元素。和棧一樣,隊列也是一種運算受限制的線性表,所以又叫先進先出表(First In First Out),簡稱FIFO表。
順序隊列
假設一個隊列有n個元素,則順序存儲的隊列需要建立一個大於n的數組,並把隊列的所有元素存儲在數組的前n個單元,數組下標爲0的一端即爲隊頭。
所謂的入隊,就是在隊尾追加一個元素,不需要移動任何元素,所以時間複雜度爲O(1).
隊列的出隊是在隊頭,即下標爲0的位置,也就意味着,隊列中的所有位置都得向前移動,以保證下標爲0的位置,即對頭不爲空。此時時間複雜度爲O(n)。
爲了避免當只有一個元素時,隊頭和隊尾重合使得處理變得麻煩,所以引入兩個指針,front指針 指向隊頭元素,rear指針 指向隊尾元素的下一個元素。這樣當 front 等於 rear 時,不是隊列中有一個元素,而是表示空隊列。
假設數組的長度爲5,空隊列及初始狀態如圖所示,front與rear指針都指向下標爲0的位置。當隊列中有4個元素時,front指針不變,rear指針指向下標爲4的位置。
此時出隊兩個元素,則front指針指向下標爲2的位置,rear不變。再入隊一個元素,front指針不變,此時rear指針移動到數組之外
假設這個隊列中的總個數不超過5個,但目前如果接着入隊的話,會導致數組越界的錯誤,但是隊列在下標爲0和1的位置是沒有元素的。我們把這種現象叫做“假溢出”。
循環隊列
解決“假溢出”的辦法就是後面滿了,就再從頭開始,也就是頭尾相接的循環。我們把隊列的這種頭尾相接的順序存儲結構稱爲循環隊列。
爲了解決“假溢出”的問題,我們引入循環隊列。
接着入隊兩個元素,會發現rear指針與front重合了。
解決辦法爲:當隊列空時,判斷條件就是 rear == front, 當隊列滿時,我們修改其判斷條件,保留一個元素空閒。也就是說,隊列滿時,數組中還有一個空閒單元。以下兩種情況,我們都認爲隊列已經滿了。
循環隊列的順序存儲結構如下:
#define SIZE 8
typedef struct Queue
{
int elem[SIZE]; // 存放隊列元素
int front; // 隊頭
int rear; // 隊尾
}Queue,*QueueS;
基本操作:
void InitQueueS(QueueS queue); // 初始化循環隊列
bool Push(QueueS queue, int val); // 入隊
bool Pop(QueueS queue, int *rtv); // 出隊
void Show(QueueS queue); // 打印隊列元素
int GetLength(QueueS queue); // 計算隊列長度
bool IsEmpty(QueueS queue); // 判空
void Clear(QueueS queue); // 清空隊列
void InitQueueS(QueueS queue)
{
assert(queue != NULL);
queue->front = 0;
queue->rear = 0;
}
static bool IsFull(QueueS queue)
{
return (queue->rear + 1) % SIZE == queue->front;
}
bool Push(QueueS queue, int val)
{
if (IsFull(queue))
{
return false;
}
queue->elem[queue->rear] = val;
queue->rear = (queue->rear + 1) % SIZE;
return true;
}
bool Pop(QueueS queue, int *rtv)
{
if (IsEmpty(queue))
{
return false;
}
if (rtv != NULL)
{
*rtv = queue->elem[queue->front];
}
queue->front = (queue->front + 1) % SIZE;
return true;
}
void Show(QueueS queue)
{
for (int i = queue->front; i != queue->rear; i = (i + 1) % SIZE)
{
printf("%d ", queue->elem[i]);
}
printf("\n");
}
int GetLength(QueueS queue)
{
return (queue->rear - queue->front + SIZE) % SIZE;
}
bool IsEmpty(QueueS queue)
{
return queue->front == queue->rear;
}
void Clear(QueueS queue)
{
queue->front = queue->rear;
}