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

Pythonでツリー(木構造)を構築し、挿入・削除・表示を操作する方法

ツリー(木構造)を構築し、要素の挿入・削除・表示といった操作を行いたい場合、必要なメソッドを備えたクラスを定義するのが一般的です。クラスのインスタンスを生成し、それを通じてノードへのアクセスや各種操作を実行します。

この記事では、対話型メニューを備えたPythonのサンプルプログラムを紹介し、その仕組みをわかりやすく解説します。

サンプルコード

class Tree_struct:
    def __init__(self, data=None, parent=None):
        self.key = data
        self.children = []
        self.parent = parent

    def set_root(self, data):
        self.key = data

    def add_node(self, node):
        self.children.append(node)

    def search_node(self, key):
        if self.key == key:
            return self
        for child in self.children:
            temp = child.search_node(key)
            if temp is not None:
                return temp
        return None

    def remove_node(self):
        parent = self.parent
        index = parent.children.index(self)
        parent.children.remove(self)
        for child in reversed(self.children):
            parent.children.insert(index, child)
            child.parent = parent

    def bfs(self):
        queue = [self]
        while queue != []:
            popped = queue.pop(0)
            for child in popped.children:
                queue.append(child)
            print(popped.key, end=' ')

my_instance = None

print('Menu (this assumes no duplicate keys)')
print('add <data> at root')
print('add <data> below <data>')
print('remove <data>')
print('display')
print('quit')

while True:
    do = input('What would you like to do? ').split()

    operation = do[0].strip().lower()
    if operation == 'add':
        data = int(do[1])
        new_node = Tree_struct(data)
        suboperation = do[2].strip().lower()
        if suboperation == 'at':
            my_instance = new_node
        elif suboperation == 'below':
            position = do[3].strip().lower()
            key = int(position)
            ref_node = None
            if my_instance is not None:
                ref_node = my_instance.search_node(key)
            if ref_node is None:
                print('No such key.')
                continue
            new_node.parent = ref_node
            ref_node.add_node(new_node)

    elif operation == 'remove':
        data = int(do[1])
        to_remove = my_instance.search_node(data)
        if my_instance == to_remove:
            if my_instance.children == []:
                my_instance = None
            else:
                leaf = my_instance.children[0]
                while leaf.children != []:
                    leaf = leaf.children[0]
                leaf.parent.children.remove_node(leaf)
                leaf.parent = None
                leaf.children = my_instance.children
                my_instance = leaf
        else:
            to_remove.remove_node()

    elif operation == 'display':
        if my_instance is not None:
            print('Breadth First Search traversal is : ', end='')
            my_instance.bfs()
            print()
        else:
            print('The tree is empty')

    elif operation == 'quit':
        break

実行結果

Menu (this assumes no duplicate keys)
add <data> at root
add <data> below <data>
remove <data>
display
quit
What would you like to do? add 5 at root
What would you like to do? add 6 below 5
What would you like to do? add 8 below 6
What would you like to do? remove 8
What would you like to do? display
Breadth First Search traversal is : 5 6
What would you like to do? quit

コードの解説

  • 必要な属性を持つ「Tree_struct」クラスを定義しています。
  • 「__init__」コンストラクタは、ノードのキー・子ノードのリスト・親ノードを初期化します。
  • 「set_root」メソッドは、ツリーのルートとなる値を設定するために用意されています。
  • 「add_node」メソッドは、ツリーに新しいノードを追加します。
  • 「search_node」メソッドは、再帰的に処理を行い、指定されたキーを持つノードを検索します。
  • 「remove_node」メソッドは、ツリーからノードを削除します。削除対象ノードの子は、その親ノードへ引き継がれます。
  • 「bfs」メソッドは、キューを利用してツリーに対して幅優先探索(BFS)を実行し、各ノードのキーを順番に出力します。
  • インスタンス変数「my_instance」が作成され、初期状態では「None」が代入されています。
  • ユーザーから実行したい操作の入力を受け付けます。
  • ユーザーの選択に応じて、対応する操作が実行されます。
  • 処理結果がコンソールに表示されます。

メニューで使えるコマンド一覧

  • add <データ> at root … 入力した値をルートとして新しいツリーを作成します。
  • add <データ> below <親の値> … 指定した親ノードの下に新しい子ノードを追加します。該当するキーが存在しない場合は「No such key.」と表示されます。
  • remove <データ> … 指定したキーのノードを削除します。
  • display … 幅優先探索(BFS)による走査結果を表示します。ツリーが空の場合は「The tree is empty」と出力されます。
  • quit … プログラムを終了します。

なお、メニューの冒頭にも表示されている通り、このプログラムはキーの重複が存在しないことを前提として設計されています。同じキーを複数登録すると、検索や削除の動作が意図どおりにならない場合があるため注意してください。

  1. Pythonで解く二分木ぬり絵ゲーム ― 後手の勝利判定アルゴリズム

    問題概要 二人のプレイヤーが二分木の上でターン制のゲームを行います。二分木の根(root)と、木のノード数nが与えられます。ここでnは奇数であり、各ノードは1からnまでの互いに異なる値を持っています。 まず先手のプレイヤーが1 ≤ x ≤ nを満たす値xを選び、続いて後手のプレイヤーがy ≠ xを満たす値yを選びます。先手は値xのノードを赤色に塗り、後手は値yのノードを青色に塗ります。 その後、先手から始めて交互に手番が進みます。各ターンで、プレイヤーは自分の色(先手なら赤、後手なら青)のノードを1つ選び、その隣接する未着色ノード(左の子・右の子・親のいずれか)を自分の色で塗ります。このような

  2. Pythonで二分木の直径を求める方法【DFSを使った実装解説】

    二分木の直径とは二分木が与えられたとき、その木の直径(diameter)を計算することを考えます。二分木の直径とは、木の中の任意の2つのノードをつなぐ最長経路の長さのことです。重要なポイントとして、この経路は必ずしも根(ルート)を通るとは限りません。例えば、次のような木を考えてみましょう。この場合、経路 [4, 2, 1, 3] または [5, 2, 1, 3] の長さが3本の辺で構成されているため、直径は3となります。解法のアプローチこの問題はDFS(深さ優先探索)を使うことで効率的に解くことができます。手順は以下の通りです。DFSで各ノードを訪問しながら直径を求めます。まず答えを格納する変