予選問題6 解答と解説


正解
  (1)
     (i) ab-c*  (ii) abcd/+e+*f/g-
  (2)
     Pascal        order(stack[stkPtr]) >= order(data[n])
     C             order(stack[stkPtr-1]) >= order(data[n])
     Quick BASIC   order(stack(stkPtr)) < order(ASC(d$))

 問5の解説で述べた「木のたどり方」(底優先探索法) を2分木の場合についてPascalのプログラム(らしきもの)で表すと

   procedure walk(T: 2分木);
   begin
      if Tが空でない then begin
         (1) Tの根をたどれ;
         (2) walk(Tの左部分木);
         (3) walk(Tの右部分木);
      end
   end;
となる(図1を参照)。
                            根
                            /\
                           /  \
                          /    \
                    左の子      右の子
                      /\          /\
                     /  \        /  \
                    /    \      /    \
                 Tの左部分木  Tの右部分木
                  /        \  /        \
                  ----------  ----------

図1(2分木 T)

                            *
                            /\
                           /  \
                          -    c
                         /\
                        /  \
                       a    b

図2(数式を表した2分木)

 数式を図2のように2分木で表したものを底優先探索法 でたどり、たどられた順に記号を並べると、演算子をオペランドの前に書いた形 の数式(これをポーランド記法の数式という) *-abc が得られる。 一方、walk()の中の(1)(2)(3)を(2)(3)(1)の 順にした方法で2分木をたどると、逆ポーランド記法の数式 ab-c* が得られる。これが(1)(i)の答である。(ii)についても各自試すとよい。

 小問(2)は、スタックstackを使って(問2の解説参照)、普通の数式を 逆ポーランド記法の数式に変換する方法に関する問題である。スタックには 演算子と左括弧(部分数式の左端を表す)だけを入れる。
 アルゴリズムは、数式を1文字ずつ読み込みながら(Pascal, Cでは第n番目の 文字がdata[n]であり、BASICでは d$=MID$(data$,n,1) である)。

 1. それがオペランド(operand)ならそのまま出力し、
 2. 演算子(operator)なら、スタックのトップにある演算子(Pascalの場合は stack[stkPtr]、Cの場合はstack[stkPtr-1]、BASICの場合はstack(stkPtr))と 優先順位を比較して表にまとめたような動作をし、
 3. 左括弧(lparen)なら、そこから部分数式がはじまるので左括弧はスタックにプッシュし、
 4. 右括弧(rparen)なら、そこで部分数式が終了したのであるから、 スタックに入っている文字を対応する左括弧が出てくるまで 全てポップして出力し、
 5. その他の場合(others)はエラーである。

入力文字 オペランド 演算子op_1 左括弧 右括弧 データ末尾
スタックトップ
演算子op_2 出力 op_1 > op_2 ならプッシュ
op_1 <= の間ポップ**
プッシュ (が現れるまでポップ 底に至るまでポップ
左括弧 出力 プッシュ プッシュ ポップ* エラー
スタックの底 出力 プッシュ プッシュ エラー 正常終了
*の付いたポップ以外は、ポップした演算子を出力する。空欄は表の**の部分に 対応する。