資料結構-Queue篇-1
前情提要 這篇文章是資料結構系列的第三篇,主要探討 Queue(佇列) 的定義、抽象資料型別(ADT)、以及不同的實作方式。今天的內容包括 線性隊列 的問題與解決方法、 環形隊列(Circular Queue) 的兩種實現,以及 鏈結串列實作的 Queue。文章還透過具體的範例程式碼展示了如何操作隊列中的新增(enqueue)和刪除(dequeue),幫助讀者更深入地理解...
前情提要
這篇文章是資料結構系列的第三篇,主要探討 Queue(佇列) 的定義、抽象資料型別(ADT)、以及不同的實作方式。今天的內容包括 線性隊列 的問題與解決方法、 環形隊列(Circular Queue) 的兩種實現,以及 鏈結串列實作的 Queue。文章還透過具體的範例程式碼展示了如何操作隊列中的新增(enqueue)和刪除(dequeue),幫助讀者更深入地理解隊列的運作與應用場景。
<!–more–>

Queue 的定義
- A queue is an ordered list in which all insertions take place at one end and all deletions take place at the opposite end. Given a queue $Q = (a_0, a_1, \dotsb, a_{n-1})$, $a_0$ 我們稱之為 front element, $a_{n-1}$ 我們稱之為 rear element,那麼 Queue 有 $\text{First-In-First-Out (FIFO)}$ 的性質。
Queue ADT
結構 : Queue
物件 : a finite ordered list with zero or more elements
函數定義
-
Queue CreateQ(max_queue_size) : 建立一個空的 queue 且最大容量為 max_queue_size。
-
Boolean IsFullQ(queue, max_queue_size) :
if (number of elements in queue == max_queue_size) return TRUE else return FALSE -
Boolean IsEmptyQ(queue) :
if (queue == CreateQ(max_queue_size)) return TRUE else return FALSE -
Queue 中新增 element
-
Queue AddQ(queue, item) 此為
Horowitz的寫法 :if (IsFullQ(queue)) queue_full else insert item at rear of queue and return queue -
ENQUEUE(Q, x) 此為
CLRS中的寫法,此為circular queue,稍後會說明到:Q[Q.tail] = x if Q.tail == Q.length Q.tail = 1 else Q.tail = Q.tail + 1
-
-
Queue 中刪除 element
-
Element DeleteQ(queue) 此為
Horowitz的寫法:if (IsEmptyQ(queue)) queue_empty else remove and return the item at front of queue -
DEQUEUE(Q) 此為
CLRS中的寫法 :x = Q[Q.head] if Q.head == Q.length Q.head = 1 else Q.head = Q.head + 1 return x
-
Queue 的實作方式
Linear Array
-
實作程式碼
#include <iostream> using namespace std; class Queue { private: int front, rear, size; int* arr; public: Queue(int maxSize) { size = maxSize; arr = new int[size]; front = -1; rear = -1; } ~Queue() { delete[] arr; } bool isFull() { return rear == size - 1; } bool isEmpty() { return front == rear; } void enqueue(int item) { if (isFull()) { cout << "Queue is full, shifting elements to the left..." << endl; shiftLeft(); } if (rear == -1) { front = 0; } arr[++rear] = item; cout << "Enqueued: " << item << endl; } int dequeue() { if (isEmpty()) { cout << "Queue is empty." << endl; return -1; } int item = arr[front]; front++; cout << "Dequeued: " << item << endl; return item; } void shiftLeft() { for (int i = front; i <= rear; i++) { arr[i - front] = arr[i]; } rear = rear - front; front = 0; } }; int main() { Queue q(3); q.enqueue(1); q.enqueue(2); q.enqueue(3); q.enqueue(4); q.dequeue(); q.dequeue(); q.enqueue(5); q.enqueue(6); return 0; } -
輸出內容
Enqueued: 1 Enqueued: 2 Enqueued: 3 Queue is full, shifting elements to the left... Enqueued: 4 Dequeued: 1 Dequeued: 2 Enqueued: 5 Enqueued: 6 -
這個方法有一個嚴重的問題如下圖 (圖 2),當 Job 進入或離開時,Queue 的索引(rear 和 front)會隨著作業的加入或刪除逐漸向右移動。當 $rear$ 等於 $\text{MAX_QUEUE_SIZE} - 1$ 時,這意味著隊列已滿。在這種情況下,必須通過
queue_full操作將整個隊列左移,這樣 $front$ 就會回到 $-1$,而 $rear$ 也需要重新計算,以確保隊列正確排列。
這是一個典型的線性隊列,而不是循環隊列。當隊列滿了並且刪除了一些元素時,隊列的空間無法立即重用,因此需要整體左移來重新排列元素,這也是該隊列的限制之一。這樣花的時間是 $O(\text{MAX_QUEUE_SIZE})$,因為每次queue_full都要將所有元素往左移動。
Array Circular queue

法一
-
實作程式碼
#include <bits/stdc++.h> using namespace std; class CircularQueue { private: int* queue; int size; int front, rear; public: CircularQueue(int n) { size = n; queue = new int[size]; front = 0; rear = 0; } bool isFull() { return (rear + 1) % size == front; } bool isEmpty() { return front == rear; } void enqueue(int value) { if (isFull()) { cout << "Queue is full" << endl; return; } queue[rear] = value; rear = (rear + 1) % size; } int dequeue() { if (isEmpty()) { cout << "Queue is empty" << endl; return -1; } int value = queue[front]; front = (front + 1) % size; return value; } void display() { if (isEmpty()) { cout << "Queue is empty" << endl; return; } int i = front; while (i != rear) { cout << queue[i] << " "; i = (i + 1) % size; } cout << endl; } ~CircularQueue() { delete[] queue; } }; int main() { ios_base::sync_with_stdio(false); cin.tie(nullptr); CircularQueue q(5); q.enqueue(10); q.enqueue(20); q.enqueue(30); q.enqueue(40); q.display(); q.dequeue(); q.display(); q.enqueue(50); q.display(); q.enqueue(60); } -
輸出結果
10 20 30 40 20 30 40 20 30 40 50 Queue is full -
使用這個方法最多可以利用 $n-1$ 的空間。
法二
-
實作程式碼
#include <iostream> using namespace std; class CircularQueue { private: int* queue; int size; int front; int rear; int tag; public: CircularQueue(int n) { size = n; queue = new int[size]; front = 0; rear = 0; tag = 0; } ~CircularQueue() { delete[] queue; } bool isFull() { return (front == rear && tag == 1); } bool isEmpty() { return (front == rear && tag == 0); } void enqueue(int value) { if (isFull()) { cout << "Queue is full" << endl; return; } queue[rear] = value; rear = (rear + 1) % size; tag = 1; } int dequeue() { if (isEmpty()) { cout << "Queue is empty" << endl; return -1; } int value = queue[front]; front = (front + 1) % size; if (front == rear) { tag = 0; } return value; } void display() { if (isEmpty()) { cout << "Queue is empty" << endl; return; } int i = front; cout << "Queue contents: "; while (i != rear || (i == rear && tag == 1)) { cout << queue[i] << " "; i = (i + 1) % size; if (i == front) break; } cout << endl; } }; int main() { CircularQueue q(5); q.enqueue(10); q.enqueue(20); q.enqueue(30); q.enqueue(40); q.enqueue(50); q.display(); cout << "Dequeued: " << q.dequeue() << endl; q.display(); q.enqueue(60); q.display(); return 0; } -
輸出內容
Queue contents: 10 20 30 40 50 Dequeued: 10 Queue contents: 20 30 40 50 10 Queue contents: 20 30 40 50 60
Link List queue
-
實作程式碼
#include <iostream> using namespace std; class Node { public: int data; Node* next; Node(int val) { data = val; next = nullptr; } }; class Queue { private: Node* front; Node* rear; public: Queue() { front = rear = nullptr; } bool isEmpty() { return front == nullptr; } void enqueue(int val) { Node* newNode = new Node(val); if (rear == nullptr) { front = rear = newNode; return; } rear->next = newNode; rear = newNode; } int dequeue() { if (isEmpty()) { cout << "Queue is empty" << endl; return -1; } Node* temp = front; front = front->next; if (front == nullptr) { rear = nullptr; } int dequeuedData = temp->data; delete temp; return dequeuedData; } int peek() { if (isEmpty()) { cout << "Queue is empty" << endl; return -1; } return front->data; } void display() { if (isEmpty()) { cout << "Queue is empty" << endl; return; } Node* current = front; while (current != nullptr) { cout << current->data << " -> "; current = current->next; } cout << "NULL" << endl; } }; int main() { Queue q; q.enqueue(10); q.enqueue(20); q.enqueue(30); q.display(); cout << "Dequeue: " << q.dequeue() << endl; q.display(); }