Python
 Computer >> コンピューター >  >> プログラミング >> Python

Pythonで二分木から「子を1つだけ持つノード」をすべて削除する方法

問題の概要

二分木のルートが与えられたとき、子を1つしか持たないノード(左右どちらか片方の子だけを持つノード)をすべて木から取り除くことを考えます。子を2つ持つノードや、子をまったく持たない葉ノードはそのまま残します。

例えば、次のような二分木が入力として与えられたとします。

Pythonで二分木から「子を1つだけ持つノード」をすべて削除する方法

この場合、出力は次のようになります。

Pythonで二分木から「子を1つだけ持つノード」をすべて削除する方法

解法のアプローチ

この問題は再帰処理を使うことでシンプルに解けます。片方の子しかないノードを見つけたら、そのノードをスキップして子を直接親につなぎ替えるイメージです。具体的には、以下の手順に従います。

  • solve() というメソッドを定義する。引数として木のルートを受け取る

  • root が null(None)の場合は、そのまま root を返す

  • root の左の子も右の子も存在しない(葉ノード)場合は、root を返す

  • root の左の子が存在しない場合は、solve(右の子) の結果を返す

  • root の右の子が存在しない場合は、solve(左の子) の結果を返す

  • root.left := solve(root.left) として左部分木を再帰的に処理する

  • root.right := solve(root.right) として右部分木を再帰的に処理する

  • 最後に root を返す

実装例(Pythonコード)

class TreeNode:
   def __init__(self, data, left = None, right = None):
      self.data = data
      self.left = left
      self.right = right

def print_tree(root):
   if root is not None:
      print_tree(root.left)
      print(root.data, end = ', ')
      print_tree(root.right)

class Solution:
   def solve(self, root):
      if not root:
         return root

      if not root.left and not root.right:
         return root

      if not root.left:
         return self.solve(root.right)

      if not root.right:
         return self.solve(root.left)

      root.left = self.solve(root.left)
      root.right = self.solve(root.right)

      return root

ob = Solution()
root = TreeNode(1)
root.left = TreeNode(2)
root.right = TreeNode(3)
root.left.left = TreeNode(4)
root.right.right = TreeNode(5)
root.left.left.right = TreeNode(6)
root.right.right.left = TreeNode(7)
root.right.right.right = TreeNode(8)

res = ob.solve(root)
print_tree(res)

入力

root = TreeNode(1)
root.left = TreeNode(2)
root.right = TreeNode(3)
root.left.left = TreeNode(4)
root.right.right = TreeNode(5)
root.left.left.right = TreeNode(6)
root.right.right.left = TreeNode(7)
root.right.right.right = TreeNode(8)

出力

6, 1, 7, 5, 8,

コードのポイント

このアルゴリズムでは、solve() メソッドが各ノードに対して再帰的に呼び出されます。重要なのは、片方の子しか持たないノードに到達した時点で、そのノード自体を返さずに、存在する側の子の再帰結果を返すという点です。これにより、該当ノードは親から切り離され、事実上削除されたことになります。

処理の流れを整理すると以下の通りです。

  • ノードが null なら何もせず終了(ベースケース)

  • 葉ノードなら削除対象ではないためそのまま残す

  • 子が1つだけあるノードは、その子以降の部分木の処理結果で置き換える

  • 子が2つあるノードは、左右それぞれの部分木を再帰的に処理してから残す

この手法により、時間計算量は木のノード数を N とすると O(N) で済みます。すべてのノードを一度ずつ訪問するだけでよいため、効率的な解法と言えます。

  1. Pythonで二分木から偶数の値を持つ葉ノードをすべて削除する方法

    二分木が与えられたとき、値が偶数であるすべての葉(リーフ)ノードを繰り返し削除する問題を考えてみましょう。削除を続けた結果、根ノードだけが残り、その値が偶数であった場合は、根ノードも併せて削除します。例えば、入力が次のような二分木だったとします。この場合、出力は次のようになります。解き方のアプローチこの問題は、後順(post-order)に近い再帰処理を使うことでシンプルに解けます。子ノードを先に処理し、その結果を受けて親ノードを判定する流れです。具体的には以下の手順に従います。関数 solve() を定義します。引数としてルートノードを受け取ります。root が null(None)の場合は

  2. Pythonで二分探索木(BST)から指定範囲外のノードをすべて削除する方法

    問題の概要二分探索木(BST)と2つの値 low、high が与えられたとき、[low, high] の範囲(境界値を含む)に該当しないノードをすべて木から削除するプログラムを作成します。例として、次のようなBSTを考えてみましょう。ここで low = 7、high = 10 とした場合、範囲外のノード(5 や 1 など)が削除され、出力は次のようになります。解法のアプローチこの問題は再帰を利用することで簡潔に解くことができます。手順は以下の通りです。関数 solve() を定義します。引数は root(現在のノード)、low、high の3つです。root が null(空)の場合は何もせず