Section 3 / 12
赤黒木の基本概念と用途
赤黒木は自己平衡二分探索木の一種であり、検索、挿入、削除の操作を効率的に行うことができます。赤黒木は、特にデータ構造が頻繁に変更される状況下でのパフォーマンスを最適化するために設計されています。この教材では、赤黒木の基本概念、特性、および実装方法について詳しく説明します。
赤黒木の基本概念
赤黒木は以下の特性を持つ二分探索木です:
- ノードの色: 各ノードは「赤」または「黒」の色を持つ。
- ルートノード: 木のルートノードは常に黒。
- 赤ノードの制約: 赤ノードの子ノードは必ず黒である。すなわち、連続して赤ノードを持つことはできない。
- 黒ノードの制約: 任意のノードからそのノードの子孫のいずれかの葉ノードまでの経路に含まれる黒ノードの数はすべて同じである。
- 葉ノード: すべての葉ノード(NILノード)は黒。
これらの特性により、赤黒木は常に平衡を保つことができ、最悪の場合でも操作の時間計算量はO(log n)となります。
赤黒木の用途
赤黒木は以下のような場面で特に有用です:
- 動的データの管理: データの挿入や削除が頻繁に発生する場合。
- オーダー付けされたデータの検索: 二分探索木の特性を持ちつつ、平衡を保つことで効率的に検索が行える。
- データベースやファイルシステム: 大量のデータを効率的に管理するため、赤黒木が利用されることが多い。
赤黒木の実装
以下にPythonでの赤黒木の基本的な実装を示します。この実装では、挿入操作を中心に解説します。
class Node:
def __init__(self, data):
self.data = data
self.color = 'red' # 新しいノードは赤
self.left = None
self.right = None
self.parent = None
class RedBlackTree:
def __init__(self):
self.NIL = Node(None) # 特殊な葉ノード
self.NIL.color = 'black'
self.root = self.NIL
def insert(self, data):
new_node = Node(data)
new_node.left = self.NIL
new_node.right = self.NIL
# 挿入する場所を見つける
parent = None
current = self.root
while current != self.NIL:
parent = current
if new_node.data < current.data:
current = current.left
else:
current = current.right
new_node.parent = parent
if parent is None: # 木が空の場合
self.root = new_node
elif new_node.data < parent.data:
parent.left = new_node
else:
parent.right = new_node
self.fix_insert(new_node)
def fix_insert(self, node):
while node != self.root and node.parent.color == 'red':
if node.parent == node.parent.parent.left:
uncle = node.parent.parent.right
if uncle.color == 'red': # Case 1: Uncle is red
node.parent.color = 'black'
uncle.color = 'black'
node.parent.parent.color = 'red'
node = node.parent.parent
else: # Case 2 and Case 3
if node == node.parent.right:
node = node.parent
self.left_rotate(node)
node.parent.color = 'black'
node.parent.parent.color = 'red'
self.right_rotate(node.parent.parent)
else:
uncle = node.parent.parent.left
if uncle.color == 'red': # Case 1: Uncle is red
node.parent.color = 'black'
uncle.color = 'black'
node.parent.parent.color = 'red'
node = node.parent.parent
else: # Case 2 and Case 3
if node == node.parent.left:
node = node.parent
self.right_rotate(node)
node.parent.color = 'black'
node.parent.parent.color = 'red'
self.left_rotate(node.parent.parent)
self.root.color = 'black'
def left_rotate(self, node):
right_child = node.right
node.right = right_child.left
if right_child.left != self.NIL:
right_child.left.parent = node
right_child.parent = node.parent
if node.parent is None:
self.root = right_child
elif node == node.parent.left:
node.parent.left = right_child
else:
node.parent.right = right_child
right_child.left = node
node.parent = right_child
def right_rotate(self, node):
left_child = node.left
node.left = left_child.right
if left_child.right != self.NIL:
left_child.right.parent = node
left_child.parent = node.parent
if node.parent is None:
self.root = left_child
elif node == node.parent.right:
node.parent.right = left_child
else:
node.parent.left = left_child
left_child.right = node
node.parent = left_child
def inorder(self, node):
if node != self.NIL:
self.inorder(node.left)
print(node.data, end=' ')
self.inorder(node.right)
# 使用例
if __name__ == "__main__":
rbt = RedBlackTree()
numbers = [10, 20, 30, 15, 25]
for num in numbers:
rbt.insert(num)
print("Inorder Traversal of Red Black Tree:")
rbt.inorder(rbt.root) # 出力: 10 15 20 25 30
コード解説
- Nodeクラス: 各ノードはデータ、色、左右の子ノード、親ノードを持ちます。
- RedBlackTreeクラス: 木の操作を行うクラスで、挿入、左回転、右回転、修正を行う関数を持っています。
- insertメソッド: 新しいノードを挿入し、木の特性を保つための修正を行います。
- fix_insertメソッド: 挿入後に赤黒木の特性を満たすようにノードの色を調整します。
- 左回転と右回転: 木の構造を変更するためのメソッドです。
- inorderメソッド: 中間順序で木のノードを出力します。
この実装を通じて、赤黒木の挿入操作とその後の修正プロセスを理解することができます。赤黒木は、挿入や削除が頻繁に行われる場合に効率的に動作するデータ構造です。