B-Tree
概要
B-Tree または B 木は自己平衡型の多分木データ構造である。各ノードがソート済みの複数のキーと子ノードへの参照を持つことで、二分木より高さの低いツリー構造を形成することができる。このため検索や挿入、削除操作の効率が良く、特にディスクや SSD のようなストレージドライブに対する I/O を最適化するように設計できることから、大量のデータを効率的に管理するようなデータベースやファイルシステムなどで広く使用されている。
Table of Contents
アルゴリズム
B-Tree における葉ノード (leaf node) またはリーフノードはツリー構造の最下層に位置し子ノードを持たないノードである。また内部ノード (internal node) は葉ノード以外の全てのノードで、少なくとも 3 つの子ノードへの参照 (ピボット) を持っている。B-Tree では内部ノードと葉ノードの両方がキーを持ち、更新操作に対してツリー全体がバランスを保ちながら効率的なデータアクセスを行える構造を維持する。
B-Tree においてルートを除くすべてのノードは少なくとも
ルートノードは例外的な扱いである。ルートが葉ノードの場合はそのキー数
内部ノードのピボットはしばしば
各ノードのキーはソートされていることから、帰納的に、部分ツリーの葉ノードの最も左に配置されているキーはその部分ツリー内で最も小さいキーであり、反対に最も右に配置されているキーは最も大きいキーである。Fig 1 に B-Tree 上のあるノードとそれを頂点とする部分ツリーを示す。ここで
B-Tree は挿入や削除のときにノードの分割やマージを行い、ツリーのバランスを保つことでツリー全体の高さ、つまり操作の最悪時間計算量を
検索操作
B-Tree の検索はバイナリツリーの検索と似ている。基本的な方針は、ノードを評価して検索対象のキー
ルートノードからキー
の探索を開始する。 キー
とノード内のキーを比較し、検索対象のキーが位置すべきインデックスを得る。ここでノード内のキーがソートされていることから二分探索を適用することができる。 二分探索で得たインデックスにキーが存在し、かつ、それが検索対象のキー
と等しい場合 ( ): キーが見つかったという結果で検索を終了する。
二分探索で得たインデックスにキーが存在しない、または、それが検索対象のキー
と異なる場合: ノードが葉ノードの場合:
キーが存在しなかったという結果で検索を終了する。
ノードが内部ノードの場合:
二分探索で得たインデックスの子ノードに移動する。
移動先の子ノードで再帰的に検索処理を実行する。
この B-Tree の検索と二分探索のアルゴリズムは次の擬似コードで表すことができる。
挿入操作
B-Tree での挿入操作はアップサート (upsert) つまり「あれば更新、なければ挿入」として実装されることが多い。挿入操作はまず挿入位置を見つける処理がルートから葉ノードの方向に伝播し、その後に分割 (split) がツリーの上方向に向かって伝播する。このため最悪計算量は木の高さに依存し
検索と同じ方法でキーが挿入されるべき葉ノードまで移動し、その適切な位置のインデックスを得る。
移動中のノードに挿入するキーと同じキーが存在する場合:
アップサート操作: そのキーに関連するデータを更新して終了する。
厳密な挿入操作: 既に同じキーが存在するという結果で終了する。
葉ノード上の適切なインデックスにキーを挿入する。
ノードに含まれているキー数が
個以下の場合: 挿入操作を終了する。
分割: ノードに含まれているキー数が
個になっている場合: ノードを
個, 個, 個の 3 つのパートに分割する。 上位の内部ノードが存在する場合 (分割したノードがルートでない場合):
中央値の 1 個のキーを上位の内部ノードに挿入する。このとき、そのキーの右に配置する子ノードへの参照は後方の
個のノードを指す。
上位ノードのキー数が
個になっている場合: 上位ノードに対して再帰的に分割処理を実行する。
挿入操作を終了する。
上位のノードが存在しない場合 (分割したノードがルートの場合):
新しいノードを作成し、中央の 1 個のキーを新しいノードに挿入し、その両側の子への参照を分割した左右のノードに向ける。
B-Tree のルートを新しいノードに設定する。
挿入操作を終了する。
削除操作
B-Tree の削除はやや複雑である (このため書籍ではしばしば省略される)。削除操作では、キーが存在するノードが葉ノードか内部ノードかによって処理が異なる。また削除後にノードのキー数が
検索と同じ方法で削除対象のキーが含まれているノードを見つける。
削除対象のキーが葉ノードに存在する場合:
葉ノードに含まれているキーを削除する。
葉ノードに対してリバランスを実行する。
削除処理を終了する。
削除対象のキーが内部ノードに存在する場合:
削除対象のキーの左の部分ツリーに属している最も右の葉ノードを検索する。
その葉ノードのキー数が
以上の場合: 削除対象のキーの右の部分ツリーに属している最も左の葉ノードを検索する。
その葉ノードのキー数が
以上の場合: 削除対象のキーの左の部分ツリーの最右の葉ノードも、右の部分ツリーの最左の葉ノードも、キーの数が
に満たない場合: predecessor または successor のどちらかのキーを削除対象のキーの位置に移動する。
キー数の減った葉ノードに対してリバランスを実行する。
削除処理を終了する。
リバランス (rebalance) はキーの削除によって一時的に不変条件を満たさなくなった (キー数が
あるノード (葉ノードまたは内部ノード) について:
ノードに含まれているキー数が
個以上の場合: リバランス処理を終了する。
ノードがルートの場合:
ノードが内部ノードで、かつ、ノードに含まれているキー数が 0 個の場合 (つまり、ただ 1 つの子ノードへの参照を持つ場合):
ノードを破棄して、子ノードを B-Tree の新しいルートとする。
リバランス処理を終了する。
ルートではなく、かつ、ノードに含まれているキー数が
未満の場合: 再配分: 右に兄弟ノードが存在し、右の兄弟ノードに含まれているキー数が
個以上の場合: 再配分: 左に兄弟ノードが存在し、左の兄弟ノードに含まれているキー数が
個以上の場合: マージ: 左右のどちらの兄弟ノードもキー数が
個以上でなかった場合:
参照
- Donald E.Knuth. The Art of Computer Programming Volume 3 Sorting and Searching Second Edition 日本語版. KADOKAWA (2015)
- Dzejla Medjedovic, Emin Tahirovic, Ines Dedovic. 大規模データセットのためのアルゴリズムとデータ構造. マイナビ出版 (2024)
- Alex Petrov. 詳説データベース ―ストレージエンジンと分散データシステムの仕組み. O'REILLY Japan (2021)




