ゆめさくエンジニア研究所
スタック問題 10

演算子を​後ろに​書く​式を計算する

演算子を​ 2 つの​数の​後ろに​書く​記法の​式を、​文字列の​並びで​受け取って​計算します。​数を​積んで​おき、​演算子が​来たら​上の​ 2 つを​取り出して​計算する、​スタックの​練習です。

問題

文字列の並び tokens を受け取ります。中身は整数か、"+" "-" "*" "/" の 4 つの演算子のどれかです。この並びを、演算子を 2 つの数の後ろに書く記法 (逆ポーランド記法と呼ばれます) の式として計算し、答えの整数を返す関数を書いてください。

  • たとえば、ふつうの書き方の「3 と 4 を足す」は、この記法では ["3", "4", "+"] と書きます
  • 割り算は、答えの小数を 0 の方へ切り捨てた整数にします。正の答えなら小数を捨て、負の答えでも 0 に近い方の整数にします
  • 0 で割る式は来ません。式は必ず正しく、最後に数が 1 つだけ残ります
  • tokens の長さは 1 以上 10000 以下です。並びに入る整数は -200 以上 200 以下、計算の途中と答えの値は、絶対値が 10 億以下です

入力と出力の例

例 1

3 と 4 を足してから、2 を掛けます。

入力

tokens = ["3","4","+","2","*"]

出力

14

例 2

7 を -2 で割ると、答えの小数は 0 の方へ切り捨てて -3 になります。

入力

tokens = ["7","-2","/"]

出力

-3

例 3

2 と 3 を掛けた 6 を、8 から引きます。

入力

tokens = ["8","2","3","*","-"]

出力

2

面接での聞かれ方

面接では、記法の名前だけで渡されることがあります。

  • 「逆ポーランド記法の式を評価してください」
  • 「割り算はどう丸めますか?」と、丸め方を自分から確かめるかを見られることがあります
  • 書き終えたあとに「ふつうの書き方の式なら、どう変わりますか?」と広げられることもあります

記法を知らなくても、例を 1 つもらえば仕組みはつかめます。割り算の丸め方と、0 で割る式が来るかどうかは、書く前に確かめておきます。

まず考える手順

段 1

まず、記法の読み方を確かめます。演算子は、すぐ前にある 2 つの値に効きます。例 3 では、"*" が 2 と 3 に効いて 6 になり、次の "-" が 8 と 6 に効いて 2 になります。

段 2

素直に書くなら、並びの中で一番左の演算子を探し、その前の 2 つと合わせて 1 つの数に置き換えることを繰り返します。正しく動きますが、置き換えのたびに並びを作り直すので、長い式では遅くなります。

段 3

演算子が使うのは「いちばん新しく出てきた 2 つの値」です。最後に入れた物を最初に取り出すので、スタックがちょうど合います。数が来たら積み、演算子が来たら上から 2 つ取り出して計算し、答えを積み直します。最後に残った 1 つが答えです。

段 4

取り出す順に気をつけます。最初に取り出すのは右側の数 b で、次が左側の数 a です。引き算と割り算は順で答えが変わるので、a から b を引き、a を b で割ります。

ここから​解答です。​先に​自分で​書いてから​読むと、​考え方が​残ります。

解答

Python

Python

def eval_postfix(tokens):
    ops = ("+", "-", "*", "/")
    stack = []
    for t in tokens:
        if t not in ops:
            stack.append(int(t))
            continue
        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:
            q = a / b
            stack.append(int(q))
    return stack[0]

JavaScript

JavaScript

function evalPostfix(tokens) {
  const ops = new Set("+-*/");
  const stack = [];
  for (const t of tokens) {
    if (!ops.has(t)) {
      stack.push(Number(t));
      continue;
    }
    const b = stack.pop();
    const a = stack.pop();
    if (t === "+") {
      stack.push(a + b);
    } else if (t === "-") {
      stack.push(a - b);
    } else if (t === "*") {
      stack.push(a * b);
    } else {
      const q = a / b;
      stack.push(Math.trunc(q));
    }
  }
  return stack[0];
}

数かどうかは、演算子の 4 つと同じ文字列かどうかで見分けています。"-2" のような負の数も、"-" とは別の文字列なので、数として積まれます。

計算量

  • 時間は O(n) です。並びを前から 1 回なめ、積むのも取り出すのも一定の手間です
  • 使う記憶は O(n) です。数ばかりが先に続く式では、ほぼ全部をスタックに積みます
  • 段 2 の置き換えを繰り返す書き方は、置き換えのたびに並びを作り直すので、時間が O(n²) になります

よくある間違い

  • Python で a // b と書く。 // は小さい方へ切り捨てるので、例 2 は -4 になってしまいます。0 の方へ切り捨てるには int(a / b) と書きます。JavaScript では Math.trunc(a / b) です
  • 取り出した 2 つの順を逆にする。最初に取り出すのは右側の数です。b - a と書くと、引き算と割り算の答えが食い違います
  • 先頭の 1 字で演算子かどうかを決める。 "-2" の先頭も - なので、負の数を演算子と取り違えます。文字列全体で比べます

面接での話し方の例

書き始める前に、方針を短く口に出します。

「演算子は、いちばん新しく出てきた 2 つの値に効くので、スタックを使います。数は積み、演算子が来たら上から 2 つ取り出して計算し、答えを積み直します。割り算は 0 の方へ切り捨てるので、Python では // ではなく int(a / b) を使います。」

書き終えたら、例を 1 つ手でたどって見せます。

「例 3 でたどります。8、2、3 を積みます。* で 3 と 2 を取り出し、2 に 3 を掛けた 6 を積みます。- で 6 と 8 を取り出し、8 から 6 を引いた 2 を積みます。最後に残った 2 を返します。」

解答を動かした版 Python 3.12.14Node.js 22.23.4確認日 2026年10月24日

コーディング面接問題の一覧へ戻る