<一覧に戻る

赤黒木の基本概念と用途

赤黒木は自己平衡二分探索木の一種であり、検索、挿入、削除の操作を効率的に行うことができます。赤黒木は、特にデータ構造が頻繁に変更される状況下でのパフォーマンスを最適化するために設計されています。この教材では、赤黒木の基本概念、特性、および実装方法について詳しく説明します。

赤黒木の基本概念

赤黒木は以下の特性を持つ二分探索木です:

  1. ノードの色: 各ノードは「赤」または「黒」の色を持つ。
  2. ルートノード: 木のルートノードは常に黒。
  3. 赤ノードの制約: 赤ノードの子ノードは必ず黒である。すなわち、連続して赤ノードを持つことはできない。
  4. 黒ノードの制約: 任意のノードからそのノードの子孫のいずれかの葉ノードまでの経路に含まれる黒ノードの数はすべて同じである。
  5. 葉ノード: すべての葉ノード(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

コード解説

  1. Nodeクラス: 各ノードはデータ、色、左右の子ノード、親ノードを持ちます。
  2. RedBlackTreeクラス: 木の操作を行うクラスで、挿入、左回転、右回転、修正を行う関数を持っています。
  3. insertメソッド: 新しいノードを挿入し、木の特性を保つための修正を行います。
  4. fix_insertメソッド: 挿入後に赤黒木の特性を満たすようにノードの色を調整します。
  5. 左回転と右回転: 木の構造を変更するためのメソッドです。
  6. inorderメソッド: 中間順序で木のノードを出力します。

この実装を通じて、赤黒木の挿入操作とその後の修正プロセスを理解することができます。赤黒木は、挿入や削除が頻繁に行われる場合に効率的に動作するデータ構造です。

出力結果: