---
# System prepended metadata

title: Queue

---

## 定義
通常討論時會唸英文，但硬要說中文就是佇列
在線性資料結構中滿足 FIFO(First In First Out)，也就是先進先出(亦後進後出)
如果用比喻來說，最常見的例子就是火車過山洞，車頭先進山洞也先出，車尾後進山洞也後出
佇列通常會使用幾個基本的功能：push()、pop()、front()、empty()、size()
* push() : 將資料推入 queue 的最後端
* pop() : 將 queue 最前端的資料取出(刪除)
* front() : 回傳 queue 最前端的資料
* empty() : 回傳 queue 是否為空
* size() : 回傳 queue 的大小

## 實作
通常會有多個方法實作，不過競程的大部分題目只需要用到 STL 內建的 queue
實際競賽上不太會需要自己搓一個，所以這裡的做法參考就好，知道怎麼做就可以了
除非你要考 APCS 的觀念題，那這裡的陣列實作建議看一下，考的機率滿高的
以下是陣列的做法，用比較偷懶的方式實作的
```cpp=
#include<bits/stdc++.h>
using namespace std ;

int Q[100] ; // queue 大小最多為 100
int f = 0, r = 0 ; // f 目前最前端的位置, r 目前最尾端的位置

void push(int data) {
    if ((r+1)%100 != f) {
        Q[r] = data ;
        r = (r+1) % 100 ;
    }
    else
        cout << "The queue is already full." ; // 裝滿的 queue 沒辦法 push
    return ;
}

void pop() {
    if (f != r) {
        Q[f] = 0 ;
        f = (f+1) % 100 ;
    }
    else
        cout << "The queue is already empty." ; // 空 queue 沒辦法 pop
    return ;
}

int bfront() {
    if (f != r)
        return Q[f] ;
    cout << "The queue is already empty." ; // 空 queue 沒辦法 front
    return -1 ; // 回傳 -1 表示 error
}

bool bempty() { // empty() 為內建函式，故取他名迴避
    return f == r ;
}

int bsize() { // size() 為內建函式，故取他名迴避
    return (r-f+100)%100 ;
}

int main() {
    ios::sync_with_stdio(0), cin.tie(0) ;

    int n, data ;
    cin >> n ; // 有 n 筆資料需要傳入 queue
    for (int i=0;i<n;i++) {
        cin >> data ;
        push(data) ;
    }

    while (!bempty()) { // 取出並輸出 queue 資料直到 queue 為空
        data = bfront() ;
        pop() ;
        cout << data << ' ' ;
    }
    return 0 ;
}
```
---

以下是 Linked List 實作的方式
```cpp=
#include<bits/stdc++.h>
using namespace std ;

struct Node { // Linked List 的結構
    int data ;
    Node* next ;
    Node* prev ;
} ;

Node* push(Node* curr, int data) {
    Node* new_node = new Node ;
    new_node->data = data ;
    new_node->next = curr ;
    new_node->prev = curr->prev ;
    curr->prev = new_node ;
    return new_node ;
}

Node* pop(Node* curr) {
    if (curr->data != -1) { // 不是 head
        Node* now = curr ;
        now->next->prev = now->prev ;
        now->prev->next = now->next ;
        curr = curr->prev ;
        delete now ;
    }
    else
        cout << "The queue is already empty." ; // 空 queue 沒辦法 pop
    return curr ;
}

int bfront(Node* curr) {
    if (curr->data != -1) { // 不是 head
        return curr->data ;
    }
    cout << "The queue is already empty." ; // 空 queue 沒辦法 front
    return -1 ; // 回傳 -1 表示 error
}

bool bempty(Node* curr) { // empty() 為內建函式，故取他名迴避
    return curr->data == -1 ;
}

int bsize(Node* curr) { // size() 為內建函式，故取他名迴避
    int len = 0 ;
    while (curr->data != -1)
        curr = curr->next, len++ ;
    return len ;
}

int main() {
    ios::sync_with_stdio(0), cin.tie(0) ;

    // 初始化
    Node *head = new Node ;
    head->data = -1 ;
    head->prev = head->next = head ;

    // 最尾端
    Node *curr = head ;

    int n, data ;
    cin >> n ; // 有 n 筆資料需要傳入 queue
    for (int i=0;i<n;i++) {
        cin >> data ;
        curr = push(curr, data) ;
    }

    curr = head->prev ;
    while (!bempty(curr)) { // 取出並輸出 queue 資料直到 queue 為空
        data = bfront(curr) ;
        curr = pop(curr) ;
        cout << data << ' ' ;
    }
    return 0 ;
}
```
## STL queue 使用方法
其實最主要的還是競賽時的使用，通常都是使用 STL 內建的 queue 來操作，以下是實際用法範例
```cpp=
#include<bits/stdc++.h>
using namespace std ;

int main() {
    ios::sync_with_stdio(0), cin.tie(0) ;

    queue<int> que ; // 宣告一個儲存 int 的 queue

    int n, data ;
    cin >> n ;
    for (int i=0;i<n;i++) {
        cin >> data ;
        que.push(data) ; // push data 進 queue
    }

    while (!que.empty()) { // 同 while (que.size() > 0)
        cout << que.front() << ' ' ; // 輸出 top
        que.pop() ;                 // 取出
    }

    return 0 ;
}
```
## 例題-ZJ e447. queue 練習
[題目連結](https://zerojudge.tw/ShowProblem?problemid=e447)
解題思路 :
主要是幫助各位練習 STL 裡面的 queue 怎麼使用
```cpp=
#include<bits/stdc++.h>
using namespace std ;

int main() {
    ios::sync_with_stdio(0), cin.tie(0) ;

    int n ;
    cin >> n ;

    queue<int> que ;
    for (int i=0, k;i<n;i++) {
        cin >> k ;
        if (k == 1) { // 推入 k
            cin >> k ;
            que.push(k) ;
        }
        else if (k == 2) {
            if (que.empty()) // 沒元素輸出 -1
                cout << "-1\n" ;
            else              // 輸出最頂端
                cout << que.front() << '\n' ;
        }
        else {
            if (!que.empty()) // 有元素才可刪除
                que.pop() ;
        }
    }



    return 0 ;
}
```
## 例題-ZJ e155. 10935 - Throwing cards away I
[題目連結](https://zerojudge.tw/ShowProblem?problemid=e155)
解題思路 :
題目說只要兩張牌以上，就要丟掉最上方的牌，次高的牌放到牌堆最下方，直接模擬出來就好
```cpp=
#include<bits/stdc++.h>
using namespace std ;

int main() {
    ios::sync_with_stdio(0), cin.tie(0) ;

    int n ;
    while (cin >> n && n) {
        cout << "Discarded cards: " ;
        queue<int> que ;
        for (int i=1;i<=n;i++)
            que.push(i) ;
        while (que.size() > 1) { // 至少兩張才能開始丟
            cout << que.front() ;
            que.pop() ; // 最上方的丟掉
            que.push(que.front()) ; // 次上方的保留放到最後
            que.pop() ;
            if (que.size() > 1) // 只要兩張以上就還能丟棄
                cout << ", " ;
            else break ; // 剩下一張所以無法丟棄
        }
        cout << "\nRemaining card: " << que.front() << '\n' ;
    }


    return 0 ;
}
```
## 例題-ZJ e564. 00540 - Team Queue
[題目連結](https://zerojudge.tw/ShowProblem?problemid=e564)
解題思路 :
這題比較複雜，我們先把結構拆開來說，Team Queue 其實就是 queue 裡面放 queue
所以在 push 元素的時候，要判斷有沒有同 team 的成員在 Team queue 裡面
如果有就將該元素放在同 team 的成員後方，其實 team 在 Team queue 裡就是 queue
只要找到有同成員的 queue，就將元素 push 進這個 queue 中
至於 pop 元素的部分，因為整個 team queue 是由很多 queue 組成的
所以會從最前方的 queue 取值，如果最前方的 queue 取完值後是空的，那這個 queue 也要被 pop
不過這種模擬實作起來會有點麻煩，所以我們把 Team queue 拆成兩個部分
1. Team queue 有哪些 team (需要照順序儲存，所以用 queue)
2. 在 Team queue 裡的 team 有哪些成員目前在 Team queue 中

這樣我們在 push 的時候，判斷有沒有同 team 的成員，只要看 2. 就行
pop 的時候也可以先從 1. 知道最前方的 team，然候從 2. 取出值，再判斷 team 成員是不是都跑了
(注意這裡因為有多組測資，所以結構用完後需要清除裡面的資料)
```cpp=
#include<bits/stdc++.h>
using namespace std ;

int t, n, x, k = 0 ;
int id[1000005] ;       // id[a] = b, 數字 a 在隊伍 b
queue<int> team[1005] ; // team[a] = {b} 隊伍 a 有 b 在 queue 中
queue<int> que ;        // que 不同隊伍在 queue 中的順序

int main() {
    ios::sync_with_stdio(0), cin.tie(0) ;
    while (cin >> t && t) {
        cout << "Scenario #" << ++k << '\n' ;
        for (int i=1;i<=t;i++) {
            cin >> n ;
            for (int j=0;j<n;j++) {
                cin >> x ;
                id[x] = i ; // 數字 x 在隊伍 b
            }
        }
        string s ;
        while (cin >> s && s != "STOP") { // 輸入直到 STOP
            if (s == "ENQUEUE") { // push
                cin >> x ;
                if (team[id[x]].empty()) // 沒有同隊伍的在 queue 中
                    que.push(id[x]) ;       // 放到最後端
                team[id[x]].push(x) ;       // 放到同隊伍中
            }
            else {
                int now = que.front() ; // 最前方的 team
                cout << team[now].front() << '\n' ;
                team[now].pop() ;
                if (team[now].empty()) que.pop() ; // 全部都出去、queue 已無該隊伍
            }
        }
        cout << '\n' ;
        while (!que.empty()) { // 清除資料
            int now = que.front() ;
            while (!team[now].empty()) team[now].pop() ;
            que.pop() ;
        }
    }

    return 0 ;
}
```

## 特殊概念-用兩個 stack 模擬 queue
其實兩個 stack 能夠模擬出 queue 的功能，因為 stack1 將所有資料到另一個 stack2
最上方的元素就是變到最下方，所以原本最先進入 stack1 的資料就可以最先出去，也就是 queue
不過如果要把資料 push 進去，要先把 stack2 的資料先丟回 stack1，不然順序會出問題
```cpp=
#include<bits/stdc++.h>
using namespace std ;
typedef stack<int> STK ;

STK stk1, stk2 ;
void bpush(int data) {
    while (!stk2.empty()) {
        stk1.push(stk2.top()) ;
        stk2.pop() ;
    }
    stk1.push(data) ;
    return ;
}

void btop_and_pop() {
    while (!stk1.empty()) {
        stk2.push(stk1.top()) ;
        stk1.pop() ;
    }
    cout << stk2.top() << '\n' ;
    stk2.pop() ;

    return ;
}

int main() {
    // 測試資料
    bpush(1) ;
    bpush(2) ;
    bpush(3) ;
    btop_and_pop() ;
    bpush(4) ;
    bpush(5) ;
    btop_and_pop() ;
    bpush(6) ;
    btop_and_pop() ;
    btop_and_pop() ;
    btop_and_pop() ;
    btop_and_pop() ;
    btop_and_pop() ;

    return 0 ;
}
```
其實還有其他方式，只要可以做出 queue 就可以了

## 總結
之後還有其他演算法會用到 queue，這裡只是拿一些簡單的例題讓各位更了解 queue
唯一比較難的就是 Team queue 那題，因為模擬的複雜度比較高，但其實就是類二維 queue