Pythonで2分木を実装します。
2分木
二分木(binary tree; 二進木、バイナリツリー)は、データ構造の1つである。根付き木構造の中で、あるノード(節点 node)が持つ子の数が高々2であるものをいう。典型的には2つの子はそれぞれ「左」「右」と呼ばれる。
たとえば、二分探索や二分ヒープを実装するために使われる。
出典: フリー百科事典『ウィキペディア(Wikipedia)』
ノードクラスの定義
木構造の中のノードを定義します。ノードは、値と左の子、右の子を持ちます。
class Node(object): def __init__(self, value): self.value = value self.left = None self.right = None
2分木クラスの定義
class BinaryTree(object): def __init__(self, root): self.root = Node(root)
これにより、例えば以下のような形で、2分木を定義できます。
tree = BinaryTree(1) tree.root.left = Node(2) tree.root.right = Node(3) tree.root.left.left = Node(4) tree.root.left.right = Node(5) tree.root.right.left = Node(6) tree.root.right.right = Node(7) tree.root.right.right.right = Node(8)
二分木を巡回する方法
行きがけ順、通りがけ順、帰りがけ順探索
二分木においてはあるノードとその子孫もまた二分木を構成する。これを部分木と呼ぶ。 従って二分木を部分木に分け、再帰を用いて探索する方法は自然である。
出典: フリー百科事典『ウィキペディア(Wikipedia)』
行きがけ順 (preorder)
行きがけ順 (preorder) は、根を調べてからそれにぶらさがる部分木を調べます。
2分木クラスのメソッドとして定義します。
def preorder_print(self, start, traversal): """Root -> Left -> Right""" if start: traversal += (str(start.value) + "-") traversal = self.preorder_print(start.left, traversal) traversal = self.preorder_print(start.right, traversal) return traversal
上で定義した2分木で出力します。
print(tree.preorder_print(tree.root, '')) # 出力 1-2-4-5-3-6-7-8-
通りがけ順 (in-order)
通りがけ順 (in-order) では、片方の部分木を調べ、根を調べ、次いで反対の部分木を調べます。
2分木クラスのメソッドとして定義します。
def inorder_print(self, start, traversal): """Left -> Root -> Right """ if start: traversal = self.inorder_print(start.left, traversal) traversal += (str(start.value) + "-") traversal = self.inorder_print(start.right, traversal) return traversal
上で定義した2分木で出力します。
print(tree.inorder_print(tree.root, '')) # 出力 4-2-5-1-6-3-7-8-
帰りがけ順 (postorder)
帰りがけ順 (postorder) では、部分木を調べてからその根を調べます。
2分木クラスのメソッドとして定義します。
def postorder_print(self, start, traversal): """Left -> Right -> Root""" if start: traversal = self.postorder_print(start.left, traversal) traversal = self.postorder_print(start.right, traversal) traversal += (str(start.value) + '-') return traversal
上で定義した2分木で出力します。
print(tree.postorder_print(tree.root, '')) # 出力 4-5-2-6-8-7-3-1-
幅優先探索
深さ優先探索と対照的に、未だに訪れていないノードを、根に近い方から探索する。
出典: フリー百科事典『ウィキペディア(Wikipedia)』
キューを用いることで幅優先探索を実装します。
まずはキューを定義します。
class Queue(object): def __init__(self): self.items = [] def enqueue(self, item): self.items.insert(0, item) def dequeue(self): if not self.is_empty(): return self.items.pop() def is_empty(self): return len(self.items) == 0 def peek(self): if not self.is_empty(): return self.items[-1].value def __len__(self): return self.size() def size(self): return len(self.items)
幅優先探索を 2分木クラスのメソッドとして実装します。
def levelorder_print(self, start): if start is None: return queue = Queue() queue.enqueue(start) traversal = '' while len(queue) > 0: traversal += str(queue.peek()) + "-" node = queue.dequeue() if node.left: queue.enqueue(node.left) if node.right: queue.enqueue(node.right) return traversal
上で定義した2分木で出力します。
print(tree.levelorder_print(tree.root)) # 出力 1-2-3-4-5-6-7-8-
葉に近いほうからの幅優先探索
逆に、葉に近いほうからの幅優先探索を定義します。
スタックとキューを用いて実装します。
スタックを定義します。
class Stack(object): def __init__(self): self.items = [] def push(self, item): self.items.append(item) def pop(self): if not self.is_empty(): return self.items.pop() def is_empty(self): return len(self.items) == 0 def peek(self): if not self.is_empty(): return self.items[-1] def size(self): return len(self.items) def __len__(self): return self.size()
葉に近いほうからの幅優先探索を 2分木クラスのメソッドとして実装します。
def reverse_levelorder_print(self, start): if start is None: return queue = Queue() stack = Stack() queue.enqueue(start) traversal = '' while len(queue) > 0: node = queue.dequeue() stack.push(node) if node.right: queue.enqueue(node.right) if node.left: queue.enqueue(node.left) while len(stack) > 0: node = stack.pop() traversal += str(node.value) + '-' return traversal
上で定義した2分木で出力します。
print(tree.reverse_levelorder_print(tree.root)) # 出力 8-4-5-6-7-2-3-1-
2分木の高さ
2分木の高さを再帰的に巡回することで求めます。
2分木クラスのメソッドとして実装します。
def height(self, node): if node is None: return -1 left_height = self.height(node.left) right_height = self.height(node.right) return 1 + max(left_height, right_height)
上で定義した2分木で出力します。
print(tree.height(tree.root)) # 出力 3
ノードの数
2分木のノードの数を求めます。
スタックを用いてノードの数を求める
2分木クラスのメソッドとして実装します。
def size(self): if self.root is None: return 0 stack = Stack() stack.push(self.root) size = 1 while stack: node = stack.pop() if node.left: size += 1 stack.push(node.left) if node.right: size += 1 stack.push(node.right) return size
上で定義した2分木で出力します。
print(tree.size()) # 出力 8
再帰でノードの数を求める
2分木クラスのメソッドとして実装します。
def size_recursive(self, node): if node is None: return 0 return 1 + self.size_recursive(node.left) + self.size_recursive(node.right)
上で定義した2分木で出力します。
print(tree.size_recursive(tree.root)) # 出力 8