Menu
Coddy logo textTech

スタック

最終更新

スタックとは、開いている端がちょうど1つだけのコレクションです。値を追加するときはトップに積み(プッシュ)、取り除くときは同じトップから外します(ポップ)。そのため、最後に入れた値がつねに最初に出てきます。これが後入れ先出し(LIFO)の意味であり、規則はこれだけです。上に載っているものを先にどけない限り、途中の値に手を伸ばす方法はありません。上の再生ボタンを押して、プッシュのたびに柱が伸び、ポップのたびに同じ端から縮んでいく様子を見てください。

この制約こそが要点です。どちらの操作もトップにしか触れないため、スタックがどれだけ高くなっても1回あたり O(1) であり、この予測しやすさこそ、スタックがこれほど多くの場面を支えている理由です。再帰 を動かす呼び出しスタック、エディタの取り消し履歴、パーサーでの括弧の対応チェック、そして再帰的な 深さ優先探索 をループに変える明示的なスタックなどです。取り除く端を入れ替えれば、代わりに キュー になります。

時間計算量と空間計算量

配列ベース、または連結リストベースの標準的なスタックについて:

操作計算量備考
プッシュ(push)O(1)動的配列では償却 O(1)。ときどきサイズ変更が入る。
ポップ(pop)O(1)つねにトップの要素なので、要素をずらす必要がない。
ピーク(top)O(1)取り除かずにトップを読む。
検索O(n)スタックの用途ではない。下まで順にポップしていく必要がある。
空間O(n)格納する値1つにつき1スロット。

ステップごとの手順

ステップ何が起きるか
1スタックは空の状態から始まり、トップは何も指していない。
2プッシュはトップの位置に値を書き込み、トップを1つ上へ動かす。
3さらにプッシュすると、直前の値のすぐ上に載る。
4ポップはトップにある値を読み、それからトップを1つ下へ動かす。
5返ってくるのはつねに、いちばん最近プッシュした値である。
6空のスタックをポップするのはエラーで、スタックアンダーフローと呼ばれる。だから実際のコードではまず is_empty() を確認する。

具体例

375 をプッシュし、そのあとスタックを空になるまで取り出すと:

操作スタック(下から上へ)返り値
push(3)[3]なし
push(7)[3, 7]なし
push(5)[3, 7, 5]なし
pop()[3, 7]5、いちばん新しい値
pop()[3]7
pop()[]3、いちばん古い値が最後

スタックを使うべきとき

使うべきとき避けるべきとき
いちばん新しい項目を先に取り出したいとき。取り消し、戻るボタン、括弧の対応チェックいちばん古い項目が先に必要なとき。それは キュー の仕事
再帰的なアルゴリズムを反復的なものに書き換えるときデータの途中を検索したり、添字でアクセスしたりする必要があるとき
式や JSON、HTML のような入れ子構造を解析するとき多くの読み手が任意の位置にアクセスするとき。配列やマップのほうが向いている
再調整なしで O(1) の挿入と削除を保証したいときデータをソート済みの順序で保ちたいとき。それは ヒープ や木が与えてくれる

Stackのコード

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

PythonでのStackのコード

Python
1stack = []2
3# Push three values onto the top4for value in [3, 7, 5]:5    stack.append(value)6    print(f"push {value} -> {stack}")7
8# Pop them back off: last in, first out9while stack:10    value = stack.pop()11    print(f"pop  {value} -> {stack}")12
13print("empty:", len(stack) == 0)
このコードをPythonプレイグラウンドで実行

スタックに関するよくある質問

LIFO とはどういう意味ですか?
後入れ先出し、つまりいちばん最近プッシュした値が最初にポップされる、ということです。積み重ねた皿がよく使われるたとえで、いま置いたばかりの皿を取り、いちばん下の皿は取りません。キュー はこれと逆の規律、すなわち FIFO です。
スタックとキューの違いは何ですか?
どちらの端から取り除くか、それだけです。どちらも一方の端に O(1) で追加します。スタックはその同じ端から取り除き(LIFO)、キューは反対の端から取り除きます(FIFO)。上の計算量の表を含め、それ以外はまったく同じです。
スタックの主な操作は何ですか?
push は値をトップに積み、pop はトップの値を取り除いて返し、peektop と呼ばれることもあります)は取り除かずにトップを読み、is_empty は中身が残っているかどうかを教えます。この4つはすべて O(1) です。
スタックオーバーフローとは何ですか?
空きがなくなったスタックにプッシュすることです。有名な例が呼び出しスタックです。関数呼び出しのたびにフレームが積まれるため、ベースケースに到達しない 再帰 は積み続け、ランタイムのスタック上限に達したところでプログラムがクラッシュします。その裏返しのエラー、つまり空のスタックをポップすることは、スタックアンダーフローと呼ばれます。
スタックはどのように実装されますか?
よくある実装は2通りです。動的配列は末尾で追加と削除を行い、償却 O(1) でキャッシュにも優しい方式です。Pythonの list とJavaの ArrayDeque はこの方式です。連結リストは先頭で追加と削除を行い、最悪でも O(1) でサイズ変更も不要ですが、要素ごとにポインタ分のコストがかかります。C++の std::stack はアダプタで、既定では区分化された配列である std::deque の上で動き、別のコンテナを指定することもできます。
実際のプログラムでスタックはどこに使われていますか?
関数呼び出しと 再帰 のための呼び出しスタック、取り消しとやり直しの履歴、ブラウザの戻る操作、パーサーでの式の評価と括弧の対応チェック、そして再帰的な 深さ優先探索 を反復的なループに変える明示的なスタックです。
Coddy programming languages illustration

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

始める