Tools + Simulatorsnanase.cc / tools

リスト・キュー・スタック

スタック

書き込み読み取り待機中
0 / 0 手順
速さ 速さ

回/秒

n = 要素数 * 償却
取り出す順1 要素のメモリ
–––––後入れ先出し値のみ
–––––先入れ先出し値のみ
––––両端から選択値のみ
位置を選択値のみ
位置を選択値+ポインタ 1 個
位置を選択値+ポインタ 2 個
––––先入れ先出し値のみ

バー
要素数 要素数

個

実行順

追加する位置

取り出す位置

実行計算量n = 32

読み書き
0回
読み取り
0
書き込み
0
現在の要素数
0
最大の要素数
0
平均の待ち回数
–
最長の待ち回数
–
追加順の維持率
–
取り出す順
後入れ先出し(LIFO)
1 要素のメモリ
値のみ
利用例
操作の取り消し(Undo)・関数呼び出し・括弧の対応チェック
取り出す順
先入れ先出し(FIFO)
1 要素のメモリ
値のみ
利用例
印刷キュー・幅優先探索・通信バッファ
取り出す順
両端から選択
1 要素のメモリ
値のみ
利用例
ワークスティーリング・スライディングウィンドウ
取り出す順
位置を選択
1 要素のメモリ
値のみ(予備領域あり)
利用例
表・画像・音声のデータ
取り出す順
位置を選択
1 要素のメモリ
値+ポインタ 1 個
利用例
ハッシュ表のチェイン法・空き領域の管理
取り出す順
位置を選択
1 要素のメモリ
値+ポインタ 2 個
利用例
LRU キャッシュ・テキストエディタ
取り出す順
先入れ先出し(FIFO)
1 要素のメモリ
値のみ(容量は固定)
利用例
音声の入出力・シリアル通信・ログの保持

音量 音量

dB

動作原理と式

スタックとキュー

  • スタック: 同じ端で追加と取り出しを行う。最後に追加したバーが最初に出る [1]
  • キュー: 一方の端で追加し、反対の端から取り出す。最初に追加したバーが最初に出る [1]
  • 両端キュー: 両方の端で追加と取り出しを行う [1]

この 3 つは追加と取り出しの位置を決めたもので、メモリ上の並べ方は決めていません。配列でもリストでも実装できます [2]。

配列とリスト

  • 配列: 連続した領域に並べる。n 番目の位置は計算で求まる。途中に追加すると後ろのバーをすべてずらす
  • 片方向リスト: 各要素が次の要素の位置を持つ。ポインタのつなぎ替えだけで追加できるが、n 番目へは先頭からたどる
  • 双方向リスト: 前の要素の位置も持つ。末尾を削除するときに 1 つ前をたどらなくてよい
  • リングバッファ: 配列の両端をつないで輪として使う。バーをずらさず、先頭と末尾の位置だけを進める [1]
h←(h+1)modc

記号: h は先頭の位置、c は容量です。

計算量

要素数 n が増えたときに、1 回の操作の読み書きがどう増えるかを表します。O(1) は要素数によらず一定で、O(n) は要素数に比例します [2]。「–」はその操作がないことを表します。

先頭に追加末尾に追加先頭を削除末尾を削除途中に追加途中を削除n 番目の読み取り値の探索
スタック–O(1)–O(1)––O(1)–
キュー–O(1)O(1)–––O(1)–
両端キューO(1)O(1)O(1)O(1)––––
配列O(n)償却 O(1)O(n)O(1)O(n)O(n)O(1)O(n)
片方向リストO(1)O(1)O(1)O(n)O(n)O(n)O(n)O(n)
双方向リストO(1)O(1)O(1)O(1)O(n)O(n)O(n)O(n)
リングバッファO(1)O(1)O(1)–––O(1)–

配列の再確保

配列が満杯になると、2 倍の容量の領域を確保してすべての要素をコピーします。この 1 回は要素数に比例します。ただし容量が倍々に増えるので、末尾への追加を n 回続けても、コピーする要素数の合計は 2n 未満に収まります。1 回あたりは一定なので、償却 O(1) と書きます [2]。

4+8+⋯+c<2c<2n

記号: c は最後にコピーした要素数で、n より小さい値です。

同じ計算量でも速さは違う

先頭から順に読むだけなら、配列もリストも O(n) です。ただし CPU はメモリをキャッシュラインという単位で読むので、連続した配列では続きの要素がすでにキャッシュにあります。要素が散らばったリストではその効果が小さく、実際の時間は長くなりがちです [3]。

音

音の高さはバーの高さで決めます。v はバーの値(1〜1000)です。

f=110×24(v−1)/999Hz

出典

  1. スタック・キュー・両端キュー(2.2.1)、順次割り当て(2.2.2)、連結割り当て(2.2.3)D. E. Knuth, The Art of Computer Programming, Vol. 1: Fundamental Algorithms, 3rd ed., Addison-Wesley, 1997, Sections 2.2.1–2.2.3
  2. 配列によるスタックとキュー(10.1)、連結リスト(10.2)、動的テーブルの償却計算量(16.4)T. H. Cormen, C. E. Leiserson, R. L. Rivest, C. Stein, Introduction to Algorithms, 4th ed., MIT Press, 2022, Sections 10.1, 10.2, 16.4
  3. キャッシュとメモリを読む順序による速さの違いU. Drepper, What Every Programmer Should Know About Memory, Red Hat, 2007

このツールで決めたこと

  • 読み書きの回数は、値の読み書きとポインタの書き込みを数える
  • 片方向リストの書き込みは追加 2 回、削除 1 回
  • 双方向リストの書き込みは追加 4 回、削除 2 回
  • リストの先頭と末尾の位置の更新は数えない
  • スタック・キュー・両端キューは実装を決めず、追加と取り出しをそれぞれ 1 回と数える
  • キューのバーが詰まって見えるのは図の表現で、回数には数えない
  • 配列の容量の初期値は 4。削除しても縮めない
  • リングバッファの容量は要素数+4 で固定。満杯のときは追加できない
  • 待ち回数は、追加してから取り出すまでに実行した操作の回数
  • 追加順の維持率は、続けて取り出した 2 本のうち追加順を保っている組の割合
  • 一覧の表から操作を選ぶと、要素数の 2/3 を追加した状態から始める
  • 値の探索は末尾の要素を探す場合を数える

関連ツール