Menu
Coddy logo textTech

キュー

最終更新

キューには使われる端が2つあります。新しい値は末尾に加わり、値は先頭から出ていくので、いちばん長く待った値が最初に処理されます。これが先入れ先出し(FIFO)であり、窓口に並ぶ行列とまったく同じふるまいです。最後尾に並び、先頭から順に呼ばれるからこそ、待ち時間が公平になります。上の再生ボタンを押して、値が片側から入り、反対側から出ていく様子を見てください。

それぞれの端が専用の添字またはポインタで管理されているため、どちらの操作も O(1) で、残りのデータをずらすこともありません。だからこそキューは、到着順に仕事を処理するあらゆるものを支えています。印刷ジョブ、タスクキューやメッセージキュー、リクエストのバッファ、そして 幅優先探索 などです。幅優先探索がグラフを1段ずつ訪れるのは、まさに探索の最前線をキューで保持しているからです。取り除く端を末尾に移せば、代わりに スタック になります。

時間計算量と空間計算量

リングバッファまたは連結リストで実装したキュー、つまり標準的な2つの実装について:

操作計算量備考
エンキュー(enqueue)O(1)末尾に書き込み、末尾の添字を1つ進める。
デキュー(dequeue)O(1)先頭を読み、先頭の添字を1つ進める。要素をずらす必要はない。
ピーク(front)O(1)取り除かずに先頭の値を読む。
検索O(n)キューの用途ではない。中を見るには取り出しきる必要がある。
空間O(n)待っている値1つにつき1スロット。

ステップごとの手順

ステップ何が起きるか
1キューは空の状態から始まり、先頭と末尾は同じスロットを指している。
2エンキューは末尾に値を書き込み、それから末尾を1つ進める。
3さらにエンキューすると、すでに待っている値の後ろに並ぶ。
4デキューは先頭にある値を読み、それから先頭を1つ進める。
5返ってくるのはつねに、いちばん長く待っていた値である。
6先頭が末尾に追いつくとキューはふたたび空になり、そこからさらにデキューするとエラーになる。

具体例

375 をエンキューし、そのあとキューを空になるまで取り出すと:

操作キュー(先頭から末尾へ)返り値
enqueue(3)[3]なし
enqueue(7)[3, 7]なし
enqueue(5)[3, 7, 5]なし
dequeue()[7, 5]3、いちばん古い値
dequeue()[5]7
dequeue()[]5、いちばん新しい値が最後

キューを使うべきとき

使うべきとき避けるべきとき
到着順に処理しなければならないとき。ジョブキュー、リクエストのバッファ、印刷スプーラいちばん新しい項目が先に必要なとき。それは スタック の仕事
幅優先探索 のように1段ずつ探索していくとき到着順ではなく優先度順に処理すべきとき。ヒープ が向いている
生産者と消費者の速度が違い、あいだにバッファが必要なときデータの途中を検索したり、添字でアクセスしたりする必要があるとき
要素をずらさずに O(1) の挿入と削除を行いたいときデキューのたびに配列をずらす実装にしてしまい、O(n) になるとき

Queueのコード

Python, JavaScript, Java, C++, Cによるクリーンで実行可能なQueueの実装です。言語を選んでコードをコピーするか、Coddyプレイグラウンドに読み込んだ状態で開けます。

PythonでのQueueのコード

Python
1from collections import deque2
3queue = deque()4
5# Enqueue three values at the rear6for value in [3, 7, 5]:7    queue.append(value)8    print(f"enqueue {value} -> {list(queue)}")9
10# Dequeue them from the front: first in, first out11while queue:12    value = queue.popleft()13    print(f"dequeue {value} -> {list(queue)}")14
15print("empty:", len(queue) == 0)
このコードをPythonプレイグラウンドで実行

キューに関するよくある質問

FIFO とはどういう意味ですか?
先入れ先出し、つまりいちばん長く待った値が次に処理される、ということです。チケット窓口に並ぶ行列が日常的なたとえです。スタック はこれと逆の規律、すなわち LIFO です。
キューとスタックの違いは何ですか?
どちらの端から取り除くか、それだけです。どちらも末尾に O(1) で追加します。キューは先頭から取り除き(FIFO)、スタックは追加したのと同じ端から取り除きます(LIFO)。計算量の表は、それ以外まったく同じです。
キューの主な操作は何ですか?
enqueue は末尾に値を追加し、dequeue は先頭の値を取り除いて返し、peekfront とも呼ばれます)は取り除かずに先頭を読み、is_empty は待っているものが残っているかどうかを教えます。この4つはすべて O(1) です。
素の配列を使うと、なぜデキューが遅くなるのですか?
配列から添字 0 の要素を取り除くと、残りの要素をすべて左へずらすことになり、デキュー1回あたり O(n) になるからです。実際の実装はこれを避けるために、先頭の添字を進めるリングバッファか、先頭ポインタを持つ連結リストを使います。Python の collections.deque と Java の ArrayDeque はこれを代わりにやってくれますが、list.pop(0) はやってくれません。
循環キューとは何ですか?
固定長の配列で作ったキューで、先頭と末尾の添字が末端を越えると 0 に巻き戻るものです。デキューで空いたスロットを再利用するので、容量 n のキューは配列の末端からはみ出すことなく、いつまでも動き続けます。
実際のプログラムでキューはどこに使われていますか?
サービス間のタスクキューやメッセージキュー、印刷やジョブのスプーラ、Web サーバーのリクエストバッファ、キーボードやイベントのバッファ、生産者と消費者のパイプライン、そして 幅優先探索 です。幅優先探索で走査が1段ずつ進むのは、まさにキューがあるからです。
Coddy programming languages illustration

Coddy でアルゴリズムをマスターしよう

始める