データ構造(スタックとキュー)に関する世界観です。
データ構造 | 意味 |
---|---|
スタック | 最後に入れたものが最初に取り出される。 どんどん上に重ね、上から取る。 スタックにデータを積む操作をプッシュ、取り出す操作をポップという。 |
キュー | 最初に入れたものが最初に取り出される。 列に並んで、最初に並んだ人から順に待機する。 キューに入れる操作をエンキュー、取り出す操作をデキューという。 |
(Ruby Way 第2版 (Professional Ruby Series)を参考に執筆しました。)
メッセージキューも参照のこと。
スタックとキューは、さまざまな場面で使われます。
特に、スタックは非常に一般的で、たとえばカッコを処理する際に、最後に開いたカッコが最初に閉じたカッコに対応します。
このように、「最後が最初に対応する」という場面は、意外と多く、そのため、スタックはさまざまな用途に使われます。
一方キューについては、カーネルのような低レベルレイヤーやプリンターのようなデバイス処理の中で、サブシステム同士を繋ぐインターフェースとして使うことが多いです。
特に、効率的な処理と非効率的な処理が一緒になる時に、非同期で処理をやり取りするような場合に、キューは使われることが多いです。
たとえば、Linuxのファイルシステムの中の、ダーティビットが付けられたi-nodeを順次書き込むために使われる、I/Oリクエストキューなどがこれに当たります。
そのように、スタックやキューはわりとよく使われます。配列や連結リストやツリーなど、単純なデータ構造よりも、少し癖のある形で使われるので、習得や理解は難しいですが、それらのデータ構造と同じぐらいよく使われるデータ構造だと言えます。
2023.07.28
Perl, PHP, Python, Rubyなどでは、リストに対してポップやプッシュなどのスタックやキューとしての操作を行うことができる。
Perl、PHP(言語)、Python(データ構造)、Ruby(基本)などを参照のこと。
2023.09.05
C言語でスタックを作るのは、意外と簡単です。
まず、スタックのアドレスを保持するスタックポインタを作り、スタックの値として配列を確保します。
その上で、スタックポインタをインクリメントして配列の最後に値を追加するpush()関数と、スタックポインタをデクリメントして値を配列の最後から削除してreturnで返すpop()関数を作ればいいのです。
キューも同様の方法で作れますが、キューを作る際にはシフト(全体をひとつずつずらす操作)が必要となるので、効率的なキューを作るのは難易度が高めです。
詳しくは以下の書籍に書かれているので、参照してください。
2024.08.15
Cでキューを実装する場合、配列を用いて実装しようとすると、メモリをできるだけ効率的に確保するようにしたいなら、両端のどちらかの端で要素を追加・削除する際にシフト操作(要素を全部ひとつひとつずらす操作)が必要となるため、効率的な実装ができません。
このような場合、メモリを余剰分に確保する方法もありますが(キューの要素数の最大数が決まっていて、その数が大きすぎないのであれば、それもいいでしょう)、配列ではなく連結リストを使うことで、要素を追加・削除する際に要素とともにポインタによる紐づけを追加・削除するだけで済みます。
なので、効率的なキューを実装したいなら、配列ではなく連結リストを使うといいでしょう。
と、ここまで配列によるキューを否定する内容を書きましたが、「Code Reading」という書籍によると、リングバッファ(循環バッファ)という方法で、配列でも効率的なキューを実装できるようです。
リングバッファにおいては、配列インデックスを追加する位置と削除する位置の二つ持ちます。追加する末尾の位置をテール、削除する先頭の位置をヘッドとします(あるいはその逆でもいい)。そして、配列インデックスが配列の終端の位置に達したら、配列の先頭の位置に戻ります。そのようにすることで、配列でも効率的なキューを実装できます。
2024.10.31
以下の書籍を参考に執筆しました。