二分探索木
高速な検索・挿入・削除が可能な木構造。
二分探索木
各ノードが最大2つの子を持つ木構造。左の子は親より小さく、右の子は親より大きい。
ノード
木構造の各要素。キー、左の子、右の子を持つ。
根
木構造の最上位ノード。
葉
子を持たないノード。
中間順
左の子、親、右の子の順に走査する。
前順
親、左の子、右の子の順に走査する。
後順
左の子、右の子、親の順に走査する。
平衡
木の左右の高さがほぼ等しい状態。
AVL木
自己平衡二分探索木。挿入・削除時に自動的に平衡を保つ。
二分探索木
なぜ二分探索木が必要か?
二分探索木は、高速な検索・挿入・削除が可能な木構造です。平均的な計算量はO(log n)で、配列のO(n)よりも効率的です。特に、データの検索・挿入・削除が頻繁な場合に適しています。
いつ使うか?
- 高速な検索が必要な場合
- データの挿入・削除が頻繁な場合
- データをソート済みの状態で維持したい場合
- 範囲検索が必要な場合
実践テクニック
再帰を使う
二分探索木の操作は、再帰を使って簡潔に実装できます。挿入、検索、削除、走査など、多くの操作が再帰的に実装できます。
走査方法を理解する
二分探索木の走査方法には、中間順、前順、後順があります。中間順は、左の子、親、右の子の順に走査し、ソートされた結果を得られます。
計算量を理解する
二分探索木の操作の計算量を理解することは、パフォーマンス最適化に重要です。例:挿入は平均O(log n)、検索は平均O(log n)、走査はO(n)です。これらを理解して、適切なデータ構造を選択します。
平衡を保つ
二分探索木が平衡していない場合、最悪の計算量はO(n)になります。平衡二分探索木(AVL木、赤黒木など)を使うことで、常にO(log n)の計算量を保てます。
# 二分探索木の基本操作class Node: def __init__(self, key): self.key = key self.left = None self.right = None
class BinarySearchTree: def __init__(self): self.root = None
def insert(self, key): self.root = self._insert(self.root, key)
def _insert(self, node, key): if node is None: return Node(key) if key < node.key: node.left = self._insert(node.left, key) else: node.right = self._insert(node.right, key) return node
def search(self, key): return self._search(self.root, key)
def _search(self, node, key): if node is None or node.key == key: return node if key < node.key: return self._search(node.left, key) return self._search(node.right, key)
def delete(self, key): self.root = self._delete(self.root, key)
def _delete(self, node, key): if node is None: return node if key < node.key: node.left = self._delete(node.left, key) elif key > node.key: node.right = self._delete(node.right, key) else: # ノードが1つ以下の子を持つ場合 if node.left is None: return node.right elif node.right is None: return node.left # ノードが2つの子を持つ場合 node.key = self._min_value_node(node.right).key node.right = self._delete(node.right, node.key) return node
def _min_value_node(self, node): current = node while current.left is not None: current = current.left return current
def inorder(self): result = [] self._inorder(self.root, result) return result
def _inorder(self, node, result): if node: self._inorder(node.left, result) result.append(node.key) self._inorder(node.right, result)
# 使用例bst = BinarySearchTree()bst.insert(50)bst.insert(30)bst.insert(70)bst.insert(20)bst.insert(40)bst.insert(60)bst.insert(80)
# 検索result = bst.search(40)print(result.key if result else "Not found") # 40
# 走査(中間順)print(bst.inorder()) # [20, 30, 40, 50, 60, 70, 80]
# 削除bst.delete(30)print(bst.inorder()) # [20, 40, 50, 60, 70, 80]基本操作
二分探索木の基本操作を確認する。
# 二分探索木の基本操作class Node: def __init__(self, key): self.key = key self.left = None self.right = None
class BinarySearchTree: def __init__(self): self.root = None
def insert(self, key): self.root = self._insert(self.root, key)
def _insert(self, node, key): if node is None: return Node(key) if key < node.key: node.left = self._insert(node.left, key) else: node.right = self._insert(node.right, key) return node
def search(self, key): return self._search(self.root, key)
def _search(self, node, key): if node is None or node.key == key: return node if key < node.key: return self._search(node.left, key) return self._search(node.right, key)
def delete(self, key): self.root = self._delete(self.root, key)
def _delete(self, node, key): if node is None: return node if key < node.key: node.left = self._delete(node.left, key) elif key > node.key: node.right = self._delete(node.right, key) else: # ノードが1つ以下の子を持つ場合 if node.left is None: return node.right elif node.right is None: return node.left # ノードが2つの子を持つ場合 node.key = self._min_value_node(node.right).key node.right = self._delete(node.right, node.key) return node
def _min_value_node(self, node): current = node while current.left is not None: current = current.left return current
def inorder(self): result = [] self._inorder(self.root, result) return result
def _inorder(self, node, result): if node: self._inorder(node.left, result) result.append(node.key) self._inorder(node.right, result)
# 使用例bst = BinarySearchTree()bst.insert(50)bst.insert(30)bst.insert(70)bst.insert(20)bst.insert(40)bst.insert(60)bst.insert(80)
# 検索result = bst.search(40)print(result.key if result else "Not found") # 40
# 走査(中間順)print(bst.inorder()) # [20, 30, 40, 50, 60, 70, 80]
# 削除bst.delete(30)print(bst.inorder()) # [20, 40, 50, 60, 70, 80]計算量
二分探索木の操作の計算量を確認する。
# 二分探索木の計算量class BinarySearchTree: # ... (上記の実装を参照)
# 挿入: 平均O(log n)、最悪O(n)bst.insert(50)
# 検索: 平均O(log n)、最悪O(n)result = bst.search(40)
# 削除: 平均O(log n)、最悪O(n)bst.delete(30)
# 走査: O(n)print(bst.inorder())使用例
二分探索木の実際の使用例を確認する。
# 二分探索木の使用例# データの高速検索class BinarySearchTree: # ... (上記の実装を参照)
# 使用例bst = BinarySearchTree()bst.insert(50)bst.insert(30)bst.insert(70)
# データの検索result = bst.search(30)print(result.key if result else "Not found") # 30
# データの削除bst.delete(30)result = bst.search(30)print(result.key if result else "Not found") # Not found平衡二分探索木
平衡二分探索木(AVL木)の実装を確認する。
# 平衡二分探索木class AVLNode: def __init__(self, key): self.key = key self.left = None self.right = None self.height = 1
class AVLTree: def __init__(self): self.root = None
def insert(self, key): self.root = self._insert(self.root, key)
def _insert(self, node, key): # 標準的な挿入 if not node: return AVLNode(key) if key < node.key: node.left = self._insert(node.left, key) else: node.right = self._insert(node.right, key)
# 高さを更新 node.height = 1 + max(self._get_height(node.left), self._get_height(node.right))
# 平衡を確認 balance = self._get_balance(node)
# 不平衡な場合、回転を行う if balance > 1 and key < node.left.key: return self._right_rotate(node) if balance < -1 and key > node.right.key: return self._left_rotate(node) if balance > 1 and key > node.left.key: node.left = self._left_rotate(node.left) return self._right_rotate(node) if balance < -1 and key < node.right.key: node.right = self._right_rotate(node.right) return self._left_rotate(node)
return node
def _get_height(self, node): if not node: return 0 return node.height
def _get_balance(self, node): if not node: return 0 return self._get_height(node.left) - self._get_height(node.right)
def _left_rotate(self, z): y = z.right T2 = y.left
# 回転を行う y.left = z z.right = T2
# 高さを更新 z.height = 1 + max(self._get_height(z.left), self._get_height(z.right)) y.height = 1 + max(self._get_height(y.left), self._get_height(y.right))
return y
def _right_rotate(self, z): y = z.left T3 = y.right
# 回転を行う y.right = z z.left = T3
# 高さを更新 z.height = 1 + max(self._get_height(z.left), self._get_height(z.right)) y.height = 1 + max(self._get_height(y.left), self._get_height(y.right))
return y
def inorder(self): result = [] self._inorder(self.root, result) return result
def _inorder(self, node, result): if node: self._inorder(node.left, result) result.append(node.key) self._inorder(node.right, result)
# 使用例avl = AVLTree()avl.insert(10)avl.insert(20)avl.insert(30)avl.insert(40)avl.insert(50)avl.insert(25)
# 走査(中間順)print(avl.inorder()) # [10, 20, 25, 30, 40, 50]合格ライン
二分探索木の基本操作ができる
二分探索木の計算量を理解している
二分探索木の走査方法を理解している
平衡二分探索木を理解している
二分探索木の使い分けができる
演習課題
課題1: 二分探索木の実装
二分探索木クラスを実装し、要素の追加・削除・検索を行うメソッドを作成してください。
課題2: 走査の実装
二分探索木の走査(中間順、前順、後順)を実装してください。
課題3: 平衡二分探索木の実装
平衡二分探索木(AVL木)を実装してください。
参考文献
この記事は以下の公的ガイドライン/標準に基づいています。
- Python Data Structures - 公式ドキュメント
- Binary Search Tree - Wikipedia
- AVL Tree - Wikipedia