基本情報技術者 2014年 秋期 午前(科目A) 問05
問題文
加減乗除を組み合わせた計算式の処理において、スタックを利用するのが適している処理はどれか。
選択肢
ア:格納された計算の途中結果を、格納された順番に取り出す処理
イ:計算の途中結果を格納し、別の計算を行った後で、その計算結果と途中結果との計算を行う処理(正解)
ウ:昇順に並べられた計算の途中結果のうち、中間にある途中結果だけ変更する処理
エ:リストの中間にある計算の途中結果に対して、新たな途中結果の挿入を行う処理
加減乗除を組み合わせた計算式の処理において、スタックを利用するのが適している処理はどれか。【午前2 解説】
要点まとめ
- 結論:スタックは途中結果を一時的に積み上げ、別の計算を行った後で直近に格納した結果を取り出して結合する処理に最も適しています。
- 根拠:スタックはLIFO(最後に入れたものを先に取り出す)構造で、括弧や入れ子を伴う式の評価や中間計算のネストを自然に表現できます。
- 差がつくポイント:順序通りに取り出すFIFOやリストの中間挿入・昇順維持を要する処理とは用途が異なるため、問題文の「別の計算を行った後で結合する」に着目すると確実に選べます。
正解の理由
正解: イ
イは「計算の途中結果を格納し、別の計算を行った後で、その計算結果と途中結果との計算を行う処理」と述べています。これは典型的なLIFO的処理(入れ子構造や括弧を含む式評価)を表しており、スタックの用途に一致します。
例えば式 の評価では、内側の括弧計算結果を一時保存(push)し、別の計算(外側)を進めたあとに取り出して(pop)結合します。スタックはこの「一時保存→別処理→取り出して結合」の流れを簡潔に実現できます。
イは「計算の途中結果を格納し、別の計算を行った後で、その計算結果と途中結果との計算を行う処理」と述べています。これは典型的なLIFO的処理(入れ子構造や括弧を含む式評価)を表しており、スタックの用途に一致します。
例えば式 の評価では、内側の括弧計算結果を一時保存(push)し、別の計算(外側)を進めたあとに取り出して(pop)結合します。スタックはこの「一時保存→別処理→取り出して結合」の流れを簡潔に実現できます。
よくある誤解
- 「格納された順番に取り出す」がスタックの説明だと勘違いする:格納した順に取り出す=FIFO(キュー)を想定する表現であり、スタックは逆順(LIFO)です。
- スタックでリストの中間要素を直接変更・挿入できると思い込む:スタックは末端(トップ)以外の挿入・変更が得意ではなく、直接の中間操作には不向きです。
- 「スタック=常に高速で万能」と誤認:用途により適切な構造(キュー、配列、連結リスト、木など)を選ぶ必要があります。
解法ステップ
- 問題文から「別の計算を行った後で結合する」などのキーワードがあるか注目する。
- 「一時的に保存して後で取り出す」処理が存在すればLIFO(スタック)かFIFO(キュー)かを判定する。
- 「直近に格納したものを優先して取り出す」という性質が明示または暗示されていればスタックを選択する。
- 中間挿入や昇順維持などが要求されている選択肢はスタック以外を選ぶ。
- 各選択肢を上記基準で当てはめ、最も用途が一致するものを正解とする。
選択肢別の誤答解説
- ア: 「格納された順番に取り出す処理」→ 格納順そのまま取り出すのはFIFO(キュー)に相当します。スタックは逆順で取り出すため不適切です。
- イ: 正解。途中結果をスタックに積んでおき、別の計算後に取り出して結合する操作は典型的なLIFO利用例です。
- ウ: 「昇順に並べられた途中結果のうち中間を変更する」→ ソート済みデータの中間更新やランダムアクセスが必要で、スタックは不適。配列や平衡二分木が適します。
- エ: 「リストの中間に途中結果を挿入する」→ 中間挿入は連結リストや配列で行う操作で、スタックは末端(トップ)以外への挿入に向きません。
補足コラム
- スタックの代表的用途:逆ポーランド記法(RPN)による式評価、シャントンヤード(shunting-yard)アルゴリズムでの演算子管理、関数呼び出しのコールスタック、DFS(深さ優先探索)の実装、Undo機能など。
- 中置式をポストフィックス(RPN)に変換して評価する例:中置式 は RPN で となり、スタックで順に処理すれば簡単に評価できます。
- 簡単なRPN評価のPython例(スタック利用):
def eval_rpn(tokens):
stack = []
for t in tokens:
if t in ('+', '-', '*', '/'):
b = stack.pop()
a = stack.pop()
if t == '+':
stack.append(a + b)
elif t == '-':
stack.append(a - b)
elif t == '*':
stack.append(a * b)
else:
stack.append(a / b)
else:
stack.append(float(t))
return stack.pop()
# 例: 3 4 2 * +
print(eval_rpn(["3", "4", "2", "*", "+"])) # 出力 11.0
FAQ
- Q: スタックとキューの違いは?
A: スタックはLIFO(最後に入れたものを最初に取り出す)、キューはFIFO(最初に入れたものを最初に取り出す)です。用途が異なるため混同しないことが重要です。 - Q: 括弧を含む式評価ではなぜスタックが使われる?
A: 括弧は入れ子構造を作るため、内側の計算結果を一時保存しておき、外側で取り出して使用するというLIFOの流れが自然に対応します。 - Q: スタックで中間挿入は可能ですか?
A: 実装上は可能でも効率的ではなく、スタックの本来の用途ではありません。中間挿入が多いなら別のデータ構造を選ぶべきです。 - Q: スタックの時間計算量は?
A: push/pop は通常O(1)です。式全体の評価はトークン数に比例してO(n)になります。
関連キーワード: スタック, LIFO, 逆ポーランド記法, シャントンヤード, 演算子優先順位, 中置式評価, コールスタック, 深さ優先探索, 一時保存, 式の評価

\ せっかくなら /
基本情報技術者を
クイズ形式で学習しませんか?
クイズ画面へ遷移する→
すぐに利用可能!

