データ構造 B ツリー B+ ツリー B* ツリーの特性とルールの図

序文

B ツリーの基本ルール

  1. 各ノードには最大で m 個の子ノードがあります。m は正の整数です。ルート ノードを除いて、他のノードには少なくとも ⌈m/2⌉ の子ノードがあります。

  2. 各ノードのキー値は非降順に配置されます。ノード内のキー値 k について、n 個の子ノードがある場合、ノード内の n-1 個のキー値はノードを n 個の間隔に分割します。i 番目の間隔のキー値は k 未満であり、 i+1 番目の間隔のキー値は次のとおりです。キー値は k 以上です。

  3. すべてのリーフ ノードは同じレイヤー上に位置し、キー値情報を含まず、外部に保存されたブロックとみなすことができます。

  4. 各非リーフ ノードのキー値の数は、子ノードの数から 1 減ります。

  5. 各ノードのキー値の数は次の条件を満たします:
    ⌈m/2⌉-1 <= キー値の数 <= m-1

![ここに画像の説明を挿入](https://img-blog.csdnimg.cn/7608c2a7fa9a45efbafd86029ce6b7a1.png)

B-treeのデータ挿入(文字説明+イラスト)

挿入ルールの概要:

1. ルート ノードから開始して、ノード内のキー値に従って比較し、適切なリーフ ノードを見つけます (比較結果は二分探索ツリーに似ています)。

2. キー値がすでにリーフ ノードに存在する場合は、対応する値を更新します。

3. リーフ ノードにキー値が存在しない場合は、キー値をリーフ ノードの適切な位置に挿入し、ノード内のキー値を順序どおりに保ちます。

4. キー値を挿入した後、リーフノード内のキー値の数が上限値 m-1 を超える場合、分割操作が実行されます。

将叶子节点的键值分成两部分,左边部分包含⌈(m-1)/2⌉个键值,右边部分包含⌊(m-1)/2⌋个键值。

将右边部分的键值和对应的子节点分离出来,形成一个新的叶子节点。

将新的叶子节点插入到叶子节点的右边,并更新父节点的键值信息。

5. キー値を挿入した後、リーフノード内のキー値の数が上限値 m-1 を超えない場合は、キー値を直接挿入します。

6. 親ノードが存在し、キー値挿入後に上限値 m-1 を超えた場合は、分割操作を実行します。

将父节点的键值分成两部分,左边部分包含⌈(m-1)/2⌉个键值,右边部分包含⌊(m-1)/2⌋个键值。

将右边部分的键值和对应的子节点分离出来,形成一个新的父节点。

将新的父节点插入到父节点的右边,并更新祖父节点的键值信息。

ルート ノードに到達するまで手順 6 を繰り返します。

(イラスト) 挿入状況は以下のカテゴリーに分類されます。

  1. 親ノードがないため、分割する必要はありません。
    ここに画像の説明を挿入します

  2. 親ノードがあるため、分割する必要はありません。
    ここに画像の説明を挿入します

  3. 親ノードを分割する必要はありません。
    ここに画像の説明を挿入します

    将叶子节点的键值分成两部分,左边部分包含⌈(m-1)/2⌉个键值,右边部分包含⌊(m-	1)/2⌋个键值。
    
    将右边部分的键值和对应的子节点分离出来,形成一个新的叶子节点。
    
  4. 親ノードがあり、挿入されたノードを分割する必要があります。挿入後、親ノードを分割する必要はありません。葉ノード
    ここに画像の説明を挿入します
    のキー値は 2 つの部分に分割され、左側の部分には ⌈(m-1 )/2⌉ キー値、右側の部分には ⌊(m- 1)/2⌋ キー値が含まれます。
    右側の部分のキー値を対応する子ノードから分離して、新しいリーフ ノードを形成します。

    リーフノードの右側に新規リーフノードを挿入し、親ノードのキー値情報を更新します。

  5. 親ノードがある場合は、挿入されたノードを分割する必要があり、挿入後に親ノードを分割する必要があります。

ここに画像の説明を挿入します

将父节点的键值分成两部分,左边部分包含⌈(m-1)/2⌉个键值,右边部分包含⌊(m-1)/2⌋个键值。

将右边部分的键值和对应的子节点分离出来,形成一个新的父节点。

将新的父节点插入到父节点的右边,并更新祖父节点的键值信息。

重复以上步骤,直到根节点

Bツリーデータ検索

データ配置の形式は二分木の検索に似ているため、検索も同様です。

B ツリー効率分析

メモリと外部メモリ間の検索時間の大きな違いを無視する場合:
仮定します:
有効な値の数 = N;
次数 = M;
次に: ノードの有効な値の数 = M/2 ~ M-1;
したがって: ツリーの高さは = log{M/2} N ~ log{M-1}N;
ノード内の検索の複雑さ = O(log{2}M/2) ~ O(log{2}M- 1);
したがって、最終時間計算量 = [ O(log{2}M/2 ) ~ O(log{2}M-1 ) ] * [ log{M/2} N ~ log{M-1}N ]

しかし、実際のアプリケーションではこの限りではなく、ノードへのアクセスは実際に外部メモリにアクセスするため、実際の時間は主に上位ノードから下位ノードにアクセスする処理で消費されます。

(理解のポイント) B ツリーの検索処理において、IO 操作はノードに入る操作であり、検索処理中にディスク上のノードにアクセスする必要がある場合、ディスクからノードを読み取るための IO 操作が必要になります。ディスクをメモリに取り込みます。

したがって、主な消費時間は、[ log{M/2} N ~ log{M-1}N ] 回の外部メモリへのアクセスです。

N = 62*1000000000 ノードの場合、次数 M が 1024 の場合、log M / 2 N log_{M/2}Nログ_ _M / 2N <=
4、つまり 620 億個の要素のうち、このツリーの次数が 1024 の場合、ノードを見つけるのにかかる時間は 4 回未満で、二分探索を使用して要素をすばやく見つけ出すことで、読み取り数が大幅に削減されます。ディスクの。

Bツリーの役割

データベース インデックス: B ツリーはデータベースのインデックス構造としてよく使用され、データ検索と範囲クエリを効率的にサポートできます。

ファイル システム: ファイル システムのディレクトリ構造には B ツリーが使用され、ファイルの検索と管理を効率的にサポートできます。

キャッシュ システム: B ツリーを使用してシステム内のインデックス構造をキャッシュし、データ アクセスとクエリを高速化できます。

ルーティング テーブル: B ツリーを使用してルーティング テーブルを保存および検索し、ターゲット アドレスをすばやく見つけることができます。

B+ ツリーの基本ルール

  1. ブランチノードのサブツリーポインタはキーワードの数と同じです
  2. ブランチ ノードのサブツリー ポインタ p[i] は、[k[i]、k[i+1]] の間のキー値の範囲を指します)
  3. すべてのリーフ ノードは、リンク ポインターを追加することによって相互にリンクされます。
    すべてのキーワードとそのマッピング データはリーフ ノードに表示されます (すべてのデータは最下層に保存されます)。
  4. すべてのキーワードはリーフ ノードのリンク リストに表示され、リンク リスト内のノードは順序付けされます。
  5. ブランチノード内のデータにヒットすることはできません(すべてのデータは最下層に格納されます)
  6. ブランチノードはリーフノードのインデックスに相当し、リーフノードはデータが格納されるデータ層です。
    ここに画像の説明を挿入します

B+ツリーとBツリーの比較

結論から先に言いますと、
1. ランダム検索には B-tree が、範囲検索には B+ ツリーが適しています
2. B+ ツリーは比較的省スペースです 3.
データの挿入と削除の場合、B+ ツリーは理由
:

  1. B ツリーの各ノードには、データの保存とインデックスの構築に使用されるキーワードと対応するポインターが含まれています。B ツリーのノードはデータを直接格納できるため、検索時にデータが配置されているノードを直接見つけることができます。
    B+ ツリーの各ノードにはキーワードのみが含まれ、データはリーフ ノードに格納されます。リーフ ノードはポインタを介して接続されて順序付きリンク リストを形成し、リーフ ノード上のキーワードも順序付きシーケンスを形成します。B+ ツリーの非リーフ ノードはインデックス作成にのみ使用され、データは保存されないため、非リーフ ノードの数が減り、メモリ使用率が向上します。
  2. B ツリーのノードはデータとインデックスの両方を保存するため、各ノードはより多くのデータを保存できます。これにより、ツリーの高さが減り、クエリの効率が向上します。ただし、B ツリーのノードにはデータが含まれているため、挿入および削除操作にはデータの移動と調整が必要となり、比較的時間がかかります。
    B+ ツリーの非リーフ ノードはインデックス作成にのみ使用され、データは保存されないため、各ノードはより多くのインデックスを保存できます。リーフ ノードはポインタを介して接続され、順序付きリンク リストを形成します。これにより、順次アクセスを効率的に実行できます。同時に、B+ ツリーのデータはリーフ ノードにのみ格納されるため、挿入および削除操作はインデックス ノードを調整するだけで済み、比較的高速です。

B*ツリーの基本ルール

B* ツリーは B+ ツリーを変形したもので、兄弟ノードへのポインタが B+ ツリーの非ルート ノードおよび非リーフ ノードに追加されます。
ここに画像の説明を挿入します

B* ツリーと B+ ツリーの比較

結論: B* ツリーは B+ ツリーよりもスペース使用率が高い

B+ ツリーの分割: ノードがいっぱいの場合、新しいノードを割り当て、元のノードのデータの 1/2 を新しいノードにコピーし、最後に新しいノードのポインタを親ノードに追加します。

B* ツリーの分割: ノードがいっぱいで、次の兄弟ノードがいっぱいでない場合は、データの一部を兄弟ノードに移動し、元のノードにキーワードを挿入し、最後に親ノードの兄弟を変更します
。ノードのキーワード (兄弟ノードのキーワード範囲が変更されたため)。兄弟ノードもいっぱいの場合は、元のノードと兄弟ノードの間に新しいノードを追加し、データの 1/3 を新しいノードにコピーします。 . ポイントを指定し、最後に新しいノードのポインタを親ノードに追加します。

拡大する

B+ ツリーの挿入操作規則は次のとおりです。

挿入位置の検索: ルート ノードから開始して、B+ ツリーの検索ルールに従って挿入位置を検索します。挿入されたキーワードがすでにツリーに存在する場合は、特定の状況に応じて処理されます (置換、無視、マージなど)。

リーフ ノードに挿入: 新しいキーワードをリーフ ノードの適切な位置に挿入します。リーフノードの原因となるキーワードの数が挿入後の順序の上限を超えた場合、分割操作が実行されます。

リーフノード分割:順序の上限を超えたリーフノードを2つのノードに分割します。キーワードの半分を新しいノードに移動し、ポインタ接続を調整します。同時に、新しいノードの最小キーが親ノードに挿入されます。

親ノードの調整: リーフノードの分割により、親ノードのキーワードの数が順序の上限を超える場合、再帰的な分割操作が実行されます。順序の上限を超えた親ノードを2つのノードに分割し、親ノードの親ノードに新規ノードの最小キーを挿入します。

再帰的調整: 親ノードの分割により祖先ノードのキーワード数が順序の上限を超える場合、再帰的分割操作はルート ノードまで継続されます。

ルート ノードの分割: ルート ノードの分割によりツリーの高さが増加する場合は、新しいルート ノードを作成し、元のルート ノードを 2 つのノードに分割します。新しいルート ノードの最小キーを新しいルート ノードに挿入し、ポインタ接続を調整します。

おすすめ

転載: blog.csdn.net/WSK1454360679/article/details/133500802