資格道場 › レッスン一覧 › アルゴリズムとプログラミング
アルゴリズムとプログラミング・5/13・12分

スタックとキュー(LIFO と FIFO)

ゴール

スタック(LIFO)とキュー(FIFO)の違いを言い分けられ、push/pop や enq/deq の操作を紙の上で追って、取り出される値を答えられるようになる。スタックで出力できる列の判定、必要なスタック数、並べ替えの回数を問う問題も解けるようになる。

スタックとキューは、関数の呼出しやブラウザの「戻る」、処理の順番待ちなど、プログラムの裏側で常に使われている基本のデータ構造です。過去問では、用語を問う問題と操作を追う問題の両方が繰り返し出題されています。

このレッスンで稽古する(用語チェック・過去問)

結論:スタックは後入れ先出し、キューは先入れ先出し

スタックは、最後に入れたデータから取り出すデータ構造です。この性質をLIFO(Last In First Out:後入れ先出し)といいます。キュー(待ち行列)は最初に入れたデータから取り出す構造で、性質はFIFO(First In First Out:先入れ先出し)です。

  • スタックは積み重ねたお皿:洗ったお皿は上に積み、使うときも上から取ります。
  • キューはレジの行列:先に並んだ人から会計し、新しく来た人は列の後ろに並びます。
比べる点 スタック キュー(待ち行列)
取り出す順 LIFO(後入れ先出し) FIFO(先入れ先出し)
入れる操作 push(プッシュ) enqueue(エンキュー・ENQ・enq)
取り出す操作 pop(ポップ) dequeue(デキュー・DEQ・deq)
出入口 頂上の1か所だけ 入口は末尾、出口は先頭の2か所
身近な例 積んだお皿、関数の呼出しと復帰 レジの行列、印刷の順番待ち
スタック(LIFO)123pushpop出入口は上の1か所最後の3が最初に出るキュー(FIFO)123deqenq先頭末尾出口は先頭・入口は末尾最初の1が最初に出る
スタックは上の1か所から出し入れするので最後の3が最初に出る。キューは末尾から入って先頭から出るので最初の1が最初に出る。

用語の見分け:選択肢に混ぜられる言葉

選択肢には、スタックとキュー以外のデータ構造や、つづりの似た略語が混ぜられます。それぞれ「何についての言葉か」が違うので、表で区別しておきましょう。

用語 意味 スタック・キューとの関係
LIFO 後入れ先出し スタックの性質
FIFO 先入れ先出し キューの性質
LILO 後に入れたものが後に出る 意味はFIFOと同じだが、一般的な呼び方ではない。スタックの答えにはならない
LRU 最も長い間使われていないものを追い出す方式 ページ置換えの方式で、データ構造の性質ではない
データ構造 特徴 見分けのキーワード
スタック 頂上だけでデータを出し入れする 後入れ先出し・push/pop
キュー 末尾に入れて先頭から出す 先入れ先出し・enq/deq
配列 添字(要素の番号)で要素を直接参照する 「添字を用いて参照」
木構造・リスト ポインタで要素どうしをつなぐ 「ポインタで階層関係を表現」
ヒープ 親の値が子の値より常に大きい(または小さい)木 最大値・最小値をすぐ取り出せる
ハッシュ キーから計算した位置にデータを格納する ハッシュ関数・衝突

操作を追うコツ:中身を1行ずつ書く

トレース問題(操作を順に追う問題)は、暗算せずに操作のたびにデータ構造の中身を1行で書くのがいちばん速くて確実です。書き方を先に決めておけば、上下や前後の取り違えがなくなります。

  • スタック:左端を底、右端を頂上として書く。push は右端に足し、pop は右端を消す。
  • キュー:左端を先頭、右端を末尾として書く。enq は右端に足し、deq は左端を消す。
  • 取り出した値は、行の横に「→5」のようにメモしておく。

選択肢がスタックの図(上から順に並べたもの)なら、最後に自分のメモを右端から読んで照合します。スタックとキューの両方が出てくる問題では、2つの中身を横に並べて書きます。

操作中身(左=底・右=頂上)push 33push 535pop3→5push 838pop3→8push は右端に足す/pop は右端を消してメモ
操作のたびにスタックの中身を1行で書き、右端を頂上として pop したら右端を消して取り出した値をメモする。

コードからスタックを見抜く

変数 p で位置を管理する関数の問題では、入れる位置と取り出す位置が同じ p かどうかを見ます。f(x) は「p を1増やして A[p] に入れる」、g() は「A[p] を取り出してから p を1減らす」なので、最後に入れた位置から取り出しています。つまりスタックです。

キューなら、入れる位置(末尾)と取り出す位置(先頭)を別々の変数で管理する必要があります。位置を表す変数が p の1つだけなのは、出入口が1か所しかないスタックの特徴です。

g()f(x)729A[1]A[2]A[3]A[4]A[5]pf(x):p を1増やして A[p] に入れるg():A[p] を取り出してから p を1減らす入れる所も出す所も同じ p = スタック
f(x) は p を1増やした位置に入れ、g() は今の p の位置から取り出すので、出し入れが同じ p の1か所で起きるスタックになる。

再帰呼出しとスタック(前のレッスンの続き)

関数を呼び出すとき、コンピュータは局所変数(その関数の中だけで使う変数)、仮引数(関数の側で値を受け取る変数)、戻り番地(呼び出した関数の処理が終わった後に戻る命令の位置)をスタックに積みます。再帰で何段も呼び出すとこれらが積み重なり、復帰するときは最後に積んだものから順に取り出します。だから取り出し方はLIFOで、「FIFOで取り出す」と書かれていたらそこが誤りです。

応用:出力できる順番・必要なスタック数・並べ替えの回数

出力できる列かどうかの判定は、出力列を先頭から1つずつ、実際に push/pop して作れるか試します。判定のコツは、スタックに残っている値どうしは後から積んだものから順にしか出せないことです。例えば A, B, C, D の順に到着する場合、D を出した直後に B → C の順で出すことはできません(B は C の下にあるため)。

必要なスタック数は、「待たせておいて、入ってきた順のまま出したい値」は同じスタックに入れられない、という点から数えます。キューとスタックを使った並べ替えは、キューの先頭から取り出してスタックに積み、逆の順番でキューに戻す操作を追って回数を数えます。

D を出した直後のスタックpop で D を出力DCBA次に出せるのは C(いちばん上)B は C の下→ B→C の順は不可残った値は後から積んだ順にしか出せない
A, B, C, D の順に積んで D を出した直後は C がいちばん上なので、次に B を出すことはできない。
例題 1

スタックの操作を順に追う

次の二つのスタック操作を定義する。 PUSH n: スタックにデータ(整数値 n)をプッシュする。 POP: スタックからデータをポップする。 空のスタックに対して,次の順でスタック操作を行った結果はどれか。 PUSH 1 → PUSH 5 → POP → PUSH 7 → PUSH 6 → PUSH 4 → POP → POP → PUSH 3
各選択肢は 3 段のスタックの図。上から下へ: ア 1,7,3 / イ 3,4,6 / ウ 3,7,1 / エ 6,4,3。

ア [図] スタック(上から)1, 7, 3
イ [図] スタック(上から)3, 4, 6
ウ [図] スタック(上から)3, 7, 1
エ [図] スタック(上から)6, 4, 3

出典:平成30年度 春期 基本情報技術者試験 午前 問5

解き方と答えを見る
  1. 右端を頂上として、操作のたびにスタックの中身を書く。
  2. PUSH 1 → [1]、PUSH 5 → [1, 5]
  3. POP → 5を取り出す → [1]
  4. PUSH 7 → [1, 7]、PUSH 6 → [1, 7, 6]、PUSH 4 → [1, 7, 6, 4]
  5. POP → 4を取り出す → [1, 7, 6]、POP → 6を取り出す → [1, 7]
  6. PUSH 3 → [1, 7, 3](左が底、右が頂上)
  7. 選択肢の図は上から並んでいるので、メモを右端から読むと 3, 7, 1。

答え:ウ(上から 3, 7, 1)

例題 2

キューの操作を順に追う

待ち行列に対する操作を,次のとおり定義する。 ENQ n :待ち行列にデータ n を挿入する。 DEQ :待ち行列からデータを取り出す。 空の待ち行列に対し,ENQ 1,ENQ 2,ENQ 3,DEQ,ENQ 4,ENQ 5,DEQ,ENQ 6,DEQ,DEQ の操作を行った。次に DEQ 操作を行ったとき,取り出されるデータはどれか。

ア 1
イ 2
ウ 5
エ 6

出典:平成30年度 秋期 基本情報技術者試験 午前 問5

解き方と答えを見る
  1. 左端を先頭として、操作のたびにキューの中身を書く。
  2. ENQ 1, ENQ 2, ENQ 3 → [1, 2, 3]
  3. DEQ → 先頭の1を取り出す → [2, 3]
  4. ENQ 4, ENQ 5 → [2, 3, 4, 5]
  5. DEQ → 2を取り出す → [3, 4, 5]
  6. ENQ 6 → [3, 4, 5, 6]
  7. DEQ → 3を取り出す → [4, 5, 6]、DEQ → 4を取り出す → [5, 6]
  8. 次の DEQ では、先頭の5が取り出される。

答え:ウ(5)

例題 3

スタックとキューを同時に追う

空の状態のキューとスタックの二つのデータ構造がある。次の手続を順に実行した場合,変数 x に代入されるデータはどれか。ここで,手続で引用している関数は,次のとおりとする。 〔関数の定義〕 push(y):データ y をスタックに積む。 pop():データをスタックから取り出して,その値を返す。 enq(y):データ y をキューに挿入する。 deq():データをキューから取り出して,その値を返す。 〔手続〕 push(a) push(b) enq(pop()) enq(c) push(d) push(deq()) x ← pop()

ア a
イ b
ウ c
エ d

出典:平成26年度 春期 基本情報技術者試験 午前 問7

解き方と答えを見る
  1. スタック(右端が頂上)とキュー(左端が先頭)を並べて書く。最初はどちらも空。
  2. push(a) → スタック[a]、push(b) → スタック[a, b]
  3. enq(pop()):pop で頂上の b を取り出し、キューに入れる → スタック[a]、キュー[b]
  4. enq(c) → キュー[b, c]
  5. push(d) → スタック[a, d]
  6. push(deq()):deq で先頭の b を取り出し、スタックに積む → キュー[c]、スタック[a, d, b]
  7. x ← pop():頂上の b を取り出す → x = b

答え:イ(b)

例題 4

1つのスタックで出力できる列を判定する

A,B,C,D の順に到着するデータに対して,一つのスタックだけを用いて出力可能なデータ列はどれか。

ア A,D,B,C
イ B,D,A,C
ウ C,B,D,A
エ D,C,A,B

出典:平成29年度 秋期 基本情報技術者試験 午前 問5

解き方と答えを見る
  1. 到着順は A → B → C → D。選択肢ごとに、先頭から実際に push/pop して試す。
  2. ア A, D, B, C:A を積んですぐ出す。D を出すには B, C, D を積む → [B, C, D]。D を出すと頂上は C なので、次に B は出せない → 不可。
  3. イ B, D, A, C:A, B を積んで B を出す → [A]。C, D を積んで D を出す → [A, C]。頂上は C なので A は出せない → 不可。
  4. ウ C, B, D, A:A, B, C を積んで C を出す → [A, B]。B を出す → [A]。D を積んですぐ出す → [A]。A を出す。すべて出せる → 可能。
  5. エ D, C, A, B:A〜D を積んで D, C を出す → [A, B]。頂上は B なので A は出せない → 不可。

答え:ウ(C, B, D, A)

例題 5

必要なスタックの数を数える

A,C,K,S,T の順に文字が入力される。スタックを利用して,S,T,A,C,K という順に文字を出力するために,最小限必要となるスタックは何個か。ここで,どのスタックにおいてもポップ操作が実行されたときには必ず文字を出力する。また,スタック間の文字の移動は行わない。

ア 1
イ 2
ウ 3
エ 4

出典:令和元年度 秋期 基本情報技術者試験 午前 問8

解き方と答えを見る
  1. 入力順は A → C → K → S → T、出力したい順は S → T → A → C → K。
  2. S と T は、来たらすぐ push して pop すれば出せる。どのスタックの頂上に積んでもすぐ取り出すので、ほかの文字の邪魔にならない。
  3. A, C, K は S, T より先に来るので、S, T が出るまでスタックで待たせる必要がある。
  4. A, C, K は入ってきた順のまま(A → C → K)出したい。同じスタックに2つ入れると、後から入った方が先に出てしまう。
  5. したがって A, C, K はそれぞれ別のスタックに入れる必要があり、最小で3個。

答え:ウ(3個)

例題 6

キューとスタックで並べ替える回数を数える

四つのデータ A,B,C,D がこの順に入っているキューと空のスタックがある。手続 pop_enq,deq_push を使ってキューの中のデータを D,C,B,A の順に並べ替えるとき,deq_push の実行回数は最小で何回か。ここで,pop_enq はスタックから取り出したデータをキューに入れる操作であり,deq_push はキューから取り出したデータをスタックに入れる操作である。

ア 2
イ 3
ウ 4
エ 5

出典:平成24年度 秋期 基本情報技術者試験 午前 問5

解き方と答えを見る
  1. キューの中身は先頭から A, B, C, D。目標は D, C, B, A。使える操作は、キューの先頭をスタックに移す deq_push と、スタックの頂上をキューの末尾に移す pop_enq の2つだけ。
  2. D を先頭にするには、D より前にある A, B, C をキューから外す必要がある。外す手段は deq_push しかないので、deq_push は最低3回必要。
  3. deq_push を3回行う → キュー[D]、スタック[A, B, C](右端が頂上)
  4. pop_enq を3回行う:C, B, A の順に取り出して末尾に入れる → キュー[D, C, B, A]
  5. 3回で実現でき、3回より少ない回数では不可能なので、最小は3回。

答え:イ(3回)

要点(試験直前に読む)

  • スタック=LIFO(後入れ先出し)・push/pop。キュー=FIFO(先入れ先出し)・enq/deq。
  • トレースは操作のたびに中身を1行で書く。スタックは右端が頂上、キューは左端が先頭。
  • p を増やして入れ、同じ p から取り出して減らす関数はスタック。
  • 再帰呼出しでは局所変数・仮引数・戻り番地をスタックに積み、LIFO で取り出す。
  • スタックに残っている値は、後から積んだものから順にしか出せない。出力できる列やスタック数はこのルールで判定する。

ひっかけ

  • FIFO と LIFO の入れ替え:「スタック=FIFO」「再帰からの復帰は FIFO」は誤り。Last が先に出るのがスタック。
  • LRU・LILO のダミー:LRU はページ置換えの方式で、LILO は一般的な呼び方ではない。スタックの性質を聞かれたら答えは LIFO だけ。
  • 図の上下の読み違い:選択肢のスタックの図は上が頂上。自分のメモ(右端が頂上)を右から読んで照合する。
  • スタックとキューを同時に追う問題で、deq を末尾から取ってしまう:deq は必ず先頭(いちばん古いデータ)から取り出す。
  • キューの説明にほかの構造の説明が混ぜられる:「添字で参照」は配列、「ポインタで階層関係」は木構造、「後に入れたものが先に出る」はスタック。

用語(16)

スタック stack
最後に入れたデータから順に取り出すデータ構造。
⚠ キュー:最初に入れたデータから取り出す。「後入れ先出し」ならスタック。
キュー(待ち行列) queue(まちぎょうれつ)
最初に入れたデータから順に取り出すデータ構造。
⚠ スタック:最後に入れたデータから取り出す。「先入れ先出し」ならキュー。
LIFO Last In First Out
後から入れたものが先に出る「後入れ先出し」のこと。スタックの性質。
⚠ FIFO と1文字違い。「Last が先(First)に出る」のがスタック。
FIFO First In First Out
先に入れたものが先に出る「先入れ先出し」のこと。キューの性質。
⚠ LIFO:スタックの性質。再帰呼出しの復帰は FIFO ではなく LIFO。
LILO Last In Last Out
後に入れたものが後に出ること。意味は FIFO と同じで、試験ではダミーの選択肢として出る。
⚠ LIFO:スタックの性質。スタックを問われて LILO を選ばない。
プッシュ push
スタックの頂上にデータを積む操作。
⚠ エンキュー:キューの末尾にデータを入れる操作。
ポップ pop
スタックの頂上(最後に積んだもの)からデータを取り出す操作。
⚠ デキュー:キューの先頭(最も古いもの)から取り出す。
エンキュー enqueue(ENQ・enq)
キューの末尾にデータを入れる操作。
⚠ プッシュ:スタックの頂上に積む操作。
デキュー dequeue(DEQ・deq)
キューの先頭からデータを取り出す操作。
⚠ ポップ:スタックの頂上(最後に入れたもの)から取り出す。
LRU Least Recently Used
最も長い間使われていないものを追い出す、ページ置換えなどの方式。
⚠ LIFO・FIFO:データ構造の取り出し順を表す言葉。LRU はデータ構造の性質ではない。
ヒープ heap
親の値が子の値より常に大きい(または小さい)木構造で、最大値(最小値)をすぐ取り出せる。
⚠ スタック・キュー:入れた順番で取り出す。ヒープは値の大小で取り出す。
ハッシュ hash
キーから計算した値でデータの格納位置を決める方式。
2分木 にぶんぎ
各要素が最大2つの子をもつ木構造。
戻り番地 もどりばんち
呼び出した関数の処理が終わった後に戻る命令の位置。
局所変数 きょくしょへんすう
その関数の中だけで使われる変数。
仮引数 かりひきすう
関数を定義する側で、渡された値を受け取る変数。
⚠ 実引数:関数を呼び出す側が実際に渡す値。

このレッスンの過去問

このレッスンで稽古する