Pythonで式ツリー(式木)を構築する方法:後置記法の式から式木を作るプログラム
式ツリー(Expression Tree)とは、二分木の一種で、葉ノードには演算の対象となる値(オペランド)が格納され、内部ノードには演算子が格納されるデータ構造です。
例:「4 + ((7 + 9) * 2)」という式は、次のような式ツリーで表現できます。

問題を解くためのアプローチ
与えられた式から式ツリーを構築する際には、一般的にスタックというデータ構造を使用します。まず、与えられた後置記法(ポストフィックス記法)の式を走査しながら、以下の手順を実行していきます。
- 式の中にオペランドが現れた場合は、それをノードとして作成し、スタックにプッシュします。
- 演算子が現れた場合は、その演算子をノードとして作成し、スタック上の既存ノードの子として接続します。さらに、その演算子ノード自身もスタックにプッシュします。
- 式全体の処理が完了するまで、手順1と手順2を繰り返します。
本記事の実装では、後置記法の式を右から左へ走査している点がポイントです。後置記法の最後の文字は必ず演算子になるため、それをルートノードとし、残りの文字を逆順に処理しながら「右の子 → 左の子」の順でノードを割り当てていきます。
コード例
class stack: def __init__(self): self.arr = [] def push(self, data): self.arr.append(data) def pop(self): try: return self.arr.pop(-1) except: pass def top(self): try: return self.arr[-1] except: pass def size(self): return len(self.arr) # 式ツリー用のノードクラス class node: def __init__(self, data): self.data = data self.left = None self.right = None # 式ツリークラス class exp_tree: def __init__(self, postfix_exp): self.exp = postfix_exp self.root = None self.createTree(self.exp) def isOperator(self, char): optr = [" ", "-", "*", "/", "^"] if char in optr: # 演算子の場合 return True # True を返す return False # それ以外は False を返す def createTree(self, exp): s = stack() # 子ノードが未設定の演算子ノードを管理するためのスタック self.root = node(exp[-1]) # 後置記法の最後の文字は必ず演算子 s.push(self.root) # 残りの後置記法の式を走査 for i in "".join(reversed(exp[:-1])): curr_node = s.top() if not curr_node.right: # 現在のノードの右の子が未設定の場合 temp = node(i) curr_node.right = temp if self.isOperator(i): s.push(temp) else: # 左の子が未設定の場合 temp = node(i) curr_node.left = temp # 現在のノードの子がすべて埋まったのでスタックから取り除く s.pop() if self.isOperator(i): s.push(temp) def inorder(self, head): # 式ツリーの中順走査 # 中順走査 => 左、ルート、右 if head.left: self.inorder(head.left) print(head.data, end=" ") if head.right: self.inorder(head.right) def infixExp(self): # 中順走査の結果として中置記法の式が出力される self.inorder(self.root) print() if __name__ == "__main__": postfixExp = "ab ef*g*" et = exp_tree(postfixExp) et.infixExp()
上記のコードを実行すると、次の出力が得られます。
出力
(a + b - e * f * g)
解説:
与えられた後置記法の式からツリーを構築すると、演算子が内部ノード(ルート)となり、オペランドが葉ノードとして配置された式ツリーが完成します。このツリーに対して中順走査(Inorder Traversal)——「左の子 → ルート → 右の子」の順でノードを訪問する手法——を適用することで、元の中置記法の式である「(a + b - e * f * g)」が復元されます。このように、スタックと木構造を組み合わせることで、数式の解析や評価を効率的に行うことができます。
-
Pythonで解く二分木ぬり絵ゲーム ― 後手の勝利判定アルゴリズム
問題概要 二人のプレイヤーが二分木の上でターン制のゲームを行います。二分木の根(root)と、木のノード数nが与えられます。ここでnは奇数であり、各ノードは1からnまでの互いに異なる値を持っています。 まず先手のプレイヤーが1 ≤ x ≤ nを満たす値xを選び、続いて後手のプレイヤーがy ≠ xを満たす値yを選びます。先手は値xのノードを赤色に塗り、後手は値yのノードを青色に塗ります。 その後、先手から始めて交互に手番が進みます。各ターンで、プレイヤーは自分の色(先手なら赤、後手なら青)のノードを1つ選び、その隣接する未着色ノード(左の子・右の子・親のいずれか)を自分の色で塗ります。このような
-
Pythonで二分木の直径を求める方法【DFSを使った実装解説】
二分木の直径とは二分木が与えられたとき、その木の直径(diameter)を計算することを考えます。二分木の直径とは、木の中の任意の2つのノードをつなぐ最長経路の長さのことです。重要なポイントとして、この経路は必ずしも根(ルート)を通るとは限りません。例えば、次のような木を考えてみましょう。この場合、経路 [4, 2, 1, 3] または [5, 2, 1, 3] の長さが3本の辺で構成されているため、直径は3となります。解法のアプローチこの問題はDFS(深さ優先探索)を使うことで効率的に解くことができます。手順は以下の通りです。DFSで各ノードを訪問しながら直径を求めます。まず答えを格納する変