コンテンツにスキップ

C++/標準ライブラリ/stack

出典: フリー教科書『ウィキブックス(Wikibooks)』

標準ライブラリ<stack> ヘッダー

[編集]

はじめに

[編集]

スタックの概要

[編集]

スタックは、LIFO(Last In, First Out)と呼ばれるデータ構造です。LIFOとは、「最後に追加されたものが最初に取り出される」という意味です。

スタックは、さまざまな用途に使用できます。例えば、

  • 逆ポーランド記法の評価
  • 再帰関数の呼び出し履歴の管理
  • ブラウザの戻る/進む機能の実装

スタックの利点と用途

[編集]

スタックには、以下のような利点があります。

  • シンプルで理解しやすい
  • 実装が簡単
  • メモリ効率が良い

スタックは、以下のような用途に使用できます。

  • 構文解析
  • 式の評価
  • 関数呼び出し
  • システムログ

スタックの操作方法

[編集]

スタックは以下の操作で操作できます。

push()
スタックに要素を追加する
pop()
スタックから要素を取り出す
top()
スタックのトップ要素を取得する
empty()
スタックが空かどうかを確認する
size()
スタックの要素数を取得する

スタックの定義

[編集]

<stack>ヘッダーファイルの概要

[編集]

<stack>ヘッダーファイルは、stackテンプレートクラスを宣言します。stackテンプレートクラスは、LIFOデータ構造を実装します。

stackテンプレートクラスの宣言

[編集]
template <typename T>
class stack;

この宣言は、stackテンプレートクラスが、要素型 T を持つことを意味します。

スタック要素型とコンテナー型

[編集]

stackテンプレートクラスは、以下の要素型とコンテナー型をサポートします。

要素型
任意の型
コンテナー型
デフォルトはvectordequelistも指定可能

スタックのコンストラクタとデストラクタ

[編集]

stackテンプレートクラスは以下のコンストラクタとデストラクタを定義します。

デフォルトコンストラクタ
空のスタックを作成する
コピーコンストラクタ
既存のスタックのコピーを作成する
コピー代入演算子
既存のスタックを別のスタックにコピーする
ムーブコンストラクタ
既存のスタックを別のスタックに移動する
ムーブ代入演算子
既存のスタックを別のスタックに移動する
デストラクタ
スタックを破棄する

スタックの容量と空かどうかを確認するメンバ関数

[編集]

stackテンプレートクラスは以下のメンバ関数を定義します。

empty()
スタックが空かどうかを確認する
size()
スタックの要素数を取得する

スタックの操作

[編集]

push()メンバ関数: スタックに要素を追加する

[編集]

push()メンバ関数は、スタックに要素を追加します。

stack<int> s;
s.push(1);
s.push(2);
s.push(3);

このコードは、sというスタックに1、2、3という要素を追加します。

pop()メンバ関数: スタックから要素を取り出す

[編集]

pop()メンバ関数は、スタックから要素を取り出します。

int i = s.pop();

このコードは、sというスタックからトップ要素を取り出し、iという変数に代入します。

top()メンバ関数: スタックのトップ要素を取得する

[編集]

top()メンバ関数は、スタックのトップ要素を取得します。

int i = s.top();

このコードは、sというスタックのトップ要素を取得し、iという変数に代入します。

empty()メンバ関数: スタックが空かどうかを確認する

[編集]

empty()メンバ関数は、スタックが空かどうかを確認します。

if (s.empty()) {
    // スタックが空の場合の処理
}

このコードは、sというスタックが空かどうかを確認し、空の場合は処理を行います。

size()メンバ関数: スタックの要素数を取得する

[編集]

size()メンバ関数は、スタックの要素数を取得します。

int len = s.size();

このコードは、sというスタックの要素数を変数lenに残します。

スタックのイテレータ

[編集]

stackイテレータの宣言

[編集]
template <typename T>
class stack::iterator {
    // ...
};

この宣言は、stackテンプレートクラスのイテレータ型 iterator を宣言します。

イテレータのコンストラクタとデストラクタ

[編集]

stackテンプレートクラスのイテレータは以下のコンストラクタとデストラクタを定義します。

デフォルトコンストラクタ
初期化されていないイテレータを作成する
コピーコンストラクタ
既存のイテレータのコピーを作成する
コピー代入演算子
既存のイテレータを別のイテレータにコピーする
デストラクタ
イテレータを破棄する

イテレータのインクリメントとデクリメント

[編集]

stackテンプレートクラスのイテレータは以下のインクリメントとデクリメント演算子を定義します。

++
イテレータを次の要素に移動する
--
イテレータを前の要素に移動する

イテレータの比較

[編集]

stackテンプレートクラスのイテレータは以下の比較演算子を定義します。

==
2つのイテレータが等しいかどうかを確認する
!=
2つのイテレータが等しくないかどうかを確認する
<
1つのイテレータが別のイテレータよりも小さいかどうかを確認する
<=
1つのイテレータが別のイテレータ以下かどうかを確認する
>
1つのイテレータが別のイテレータよりも大きいかどうかを確認する
>=
1つのイテレータが別のイテレータ以上かどうかを確認する

イテレータの参照解除

[編集]

stackテンプレートクラスのイテレータは以下の参照解除演算子を定義します。

*
イテレータが指している要素を取得する

スタックのアルゴリズム

[編集]

std::swap()アルゴリズム: スタックを交換する

[編集]

std::swap()アルゴリズムは、2つのスタックを交換します。

stack<int> s1, s2;
// ...
std::swap(s1, s2);

このコードは、s1s2というスタックを交換します。

std::copy()アルゴリズム: スタックをコピーする

[編集]

std::copy()アルゴリズムは、スタックを別のスタックにコピーします。

stack<int> s1, s2;
// ...
std::copy(s1.begin(), s1.end(), std::back_inserter(s2));

このコードは、s1というスタックの内容をs2というスタックにコピーします。

std::find()アルゴリズム: スタック内で要素を検索する

[編集]

std::find()アルゴリズムは、スタック内で要素を検索します。

stack<int> s;
// ...
auto it = std::find(s.begin(), s.end(), 42);
if (it != s.end()) {
    // 要素が見つかった場合の処理
}

このコードは、sというスタック内で42という要素を検索し、見つかった場合は処理を行います。

std::count()アルゴリズム: スタック内で要素の個数を数える

[編集]

std::count()アルゴリズムは、スタック内で要素の個数を数えます。

stack<int> s;
// ...
int count = std::count(s.begin(), s.end(), 42);

このコードは、sというスタック内で42という要素の個数をカウントし、countという変数に代入します。

スタックの応用例

[編集]

逆ポーランド記法の評価

[編集]

逆ポーランド記法を評価するには、スタックを使用できます。

stack<int> s;
for (const char* token : tokens) {
    if (isdigit(*token)) {
        s.push(std::stoi(token));
    } else {
      int op2 = s.pop();
      int op1 = s.pop();
      switch (*token) {
      case '+':
           s.push(op1 + op2);
           break;
      case '-':
           s.push(op1 - op2);
           break;
      case '*':
           s.push(op1 * op2);
           break;
      case '/':
           s.push(op1 / op2);
           break;
      default:
          throw std::invalid_argument("invalid token");
      }
   }
}
 
int result = s.top();

このコードは、逆ポーランド記法式を評価し、結果をresultという変数に代入します。

ブラウザの戻る/進む機能の実装

[編集]

ブラウザの戻る/進む機能を実装するには、スタックを使用できます。

std::stack<std::string> history;
 
void goBack() {
    if (history.empty()) {
        return;
    }
 
    history.pop();
    // 前のページに移動
}
 
void goForward() {
    if (history.empty()) {
        return;
    }
 
    history.pop();
    // 次のページに移動
}

このコードは、ブラウザの戻る/進む機能を実装します。historyというスタックを使用して、閲覧したページの履歴を保存します。

まとめ

[編集]

スタックの重要性と用途

[編集]

スタックは、LIFOというデータ構造を実装するシンプルで効率的なデータ構造です。スタックは、逆ポーランド記法の評価、再帰関数の呼び出し履歴の管理、ブラウザの戻る/進む機能の実装など、さまざまな用途に使用できます。

スタックの使い方

[編集]

スタックは、以下の操作で操作できます。

push()
スタックに要素を追加する
pop()
スタックから要素を取り出す
top()
スタックのトップ要素を取得する
empty()
スタックが空かどうかを確認する
size()
スタックの要素数を取得する

スタックの応用例

[編集]

スタックは、以下のような応用例があります。

  • 逆ポーランド記法の評価
  • 再帰関数の呼び出し履歴の管理
  • ブラウザの戻る/進む機能の実装
  • 構文解析
  • 式の評価
  • 関数呼び出し
  • システムログ

練習問題

[編集]

スタックを使って逆ポーランド記法を評価するプログラムを書く

[編集]

逆ポーランド記法式を入力とし、結果を出力するプログラムを書きます。

スタックを使って再帰関数を呼び出すプログラムを書く

[編集]

nを引数とし、nから1までを順に出力する再帰関数を書きます。

スタックを使ってブラウザの戻る/進む機能を実装するプログラムを書く

[編集]

ブラウザの戻る/進む機能を実装するプログラムを書きます。