赤黒の木はなぜそんなに人気があるのですか?


46

私が見るところはどこでも、データ構造は赤黒木を使用して実装されているようです(std::setC ++、SortedDictionaryC#などで)

アルゴリズムクラスで(a、b)、赤黒、AVLの各ツリーをカバーしたので、次のようになりました(教授に尋ねたり、数冊の本を読んだり、グーグルで調べたりすることからも)。

  • AVLツリーの平均深度は赤黒ツリーよりも小さいため、AVLツリーの値の検索は一貫して高速です。
  • 赤黒木は、AVL木よりもバランスをとるための構造的な変化が少ないため、挿入/削除の速度が向上する可能性があります。これは、ランタイムと実装に大きく依存するため、これはツリーの構造変更のコストに依存するため、潜在的に言っています(ツリーが不変の場合、関数型言語でも完全に異なる可能性があります)

AVLと赤黒木を比較する多くのオンラインベンチマークがありますが、私が驚いたのは、私の教授が基本的に次のいずれかを行うと言ったということです。

  • パフォーマンスについてそれほど気にかけない場合もあります。その場合、ほとんどの場合、AVLと赤黒の10〜20%の違いはまったく問題になりません。
  • または、パフォーマンスに本当に関心があります。この場合、AVLツリーと赤黒ツリーの両方を捨てて、Bツリーを使用します。Bツリーは、より良く機能するように調整できます(または(a、b)ツリー、私は)これらすべてを1つのバスケットに入れます。)

その理由は、Bツリーがメモリにデータをよりコンパクトに格納するため(1つのノードに多くの値が含まれる)、キャッシュミスがはるかに少なくなるためです。また、ユースケースに基づいて実装を微調整し、Bツリーの順序をCPUキャッシュサイズなどに依存させることもできます。

問題は、実際の最新のハードウェアでの検索ツリーのさまざまな実装の実際の使用状況を分析するソースがほとんど見つからないことです。私はアルゴリズムに関する多くの本を調べましたが、異なるツリーバリアントを一緒に比較するものは見つかりませんでした。実際のプログラムで。)

そうは言っても、上記で述べたことに基づいて、Bツリーがそれらを上回るはずであるときに、どこでも赤黒ツリーが使用されている特定の理由がありますか?(私が見つけることができる唯一のベンチマークとして、http://lh3lh3.users.sourceforge.net/udb.shtmlも示していますが、それは特定の実装の問題である可能性があります)。それとも、実装がかなり簡単であるために、誰もが赤黒ツリーを使用する理由、またはそれを別の言葉で言うと、実装が難しいのはなぜですか?

また、関数型言語の領域に移動すると、これはどのように変わりますか?ClojureとScalaはどちらもHash配列にマップされた tryを使用しているようで、Clojureは32の分岐係数を使用しています。


8
苦痛を増すために、さまざまな種類の検索ツリーを比較するほとんどの記事は、理想的な実験よりも劣っています。
ラファエル

1
私はこれを自分で理解したことはありません。私の意見では、AVLツリーは赤黒ツリーよりも簡単に実装でき(リバランスを行う場合は少ない)、パフォーマンスに大きな違いはありません。
ジョルディバーミューレン

回答:


10

引用するとの回答「にAVL木のルートから全検索と赤黒木」の質問

AVLツリーではなく赤黒ツリーを含む一部の種類のバイナリ検索ツリーでは、ツリーへの「修正」が途中で予測され、1回のトップダウンパス中に実行されるため、2回目のパスは不要です。このような挿入アルゴリズムは通常、再帰ではなくループで実装され、実際には2パスのアルゴリズムよりもわずかに高速に実行されることがよくあります。

そのため、RedBlackツリー挿入は再帰なしで実装できます。一部の CPUでは、関数呼び出しキャッシュをオーバーランすると再帰が非常に高価になります(たとえば、登録ウィンドウの使用によるSPARC

(1回の関数呼び出しを削除することで、ソフトウェアがSparcで10倍以上高速で実行されるのを確認しました。これにより、頻繁に呼び出されるコードパスがレジスタウィンドウに対して深すぎます。レジスタウィンドウの深さあなたの顧客のシステム、そしてあなたはあなたが「ホットコードパス」にいるコールスタックをどれだけ下まで知らないか、より予測しやすいように再帰を使用しません)

また、スタックを使い果たすリスクがないという利点もあります。


ただし、2 ^ 32ノードのバランスの取れたツリーでは、約32レベル以下の再帰が必要です。スタックフレームが64バイトであっても、スタック領域は2 kb以下です。それは本当に違いを生むことができますか?私はそれを疑います。
ビョルンリンドクヴィスト

@BjörnLindqvist、1990年代のSPARCプロセッサでは、共通のコードパスをスタックの深さ7から6に変更することで、10倍以上の速度が得られることがよくありました。....それはファイルを登録しなかった方法をよく読んで
イアンRingrose

9

私も最近このトピックを調査しているので、ここに私の発見がありますが、私はデータ構造の専門家ではないことに注意してください!

Bツリーをまったく使用できない場合があります。

1つの顕著なケースはstd::map、C ++ STLからのものです。標準では、insert既存のイテレータを無効にしないことが要求されています

無効化されるイテレータや参照はありません。

http://en.cppreference.com/w/cpp/container/map/insert

挿入は既存の要素を移動するため、これは実装としてのBツリーを除外します。

別の同様のユースケースは、侵入型データ構造です。つまり、ツリーのノード内にデータを保存する代わりに、構造内に子/親へのポインターを保存します。

// non intrusive
struct Node<T> {
    T value;
    Node<T> *left;
    Node<T> *right;
};
using WalrusList = Node<Walrus>;

// intrusive
struct Walrus {
    // Tree part
    Walrus *left;
    Walrus *right;

    // Object part
    int age;
    Food[4] stomach;
};

Bツリーは、ポインターのみのデータ構造ではないため、侵入型にすることはできません。

たとえば、jemallocでは、メモリの空きブロックを管理するために、侵入型の赤黒ツリーが使用されます。これは、Linuxカーネルの一般的なデータ構造でもあります。

また、「シングルパステール再帰的」実装は、可変データ構造としてのレッドブラックツリーの人気の理由ではないと考えています。

ログn

O1

O1

opendatastructuresで説明されているバリアントは、親ポインター、挿入用の再帰的ダウンパス、およびフィックスアップ用の反復ループアップパスを使用します。再帰呼び出しは末尾位置にあり、コンパイラーはこれをループに最適化します(Rustでこれを確認しました)。

O1


3

まあ、これは信頼できる答えではありませんが、バランスの取れたバイナリ検索ツリーをコーディングする必要があるときはいつでも、それは赤黒ツリーです。これにはいくつかの理由があります。

1)平均挿入コストは赤黒木では一定です(検索する必要がない場合)が、AVL木では対数です。さらに、せいぜい1つの複雑な再構築が含まれます。最悪の場合でもまだO(log N)ですが、それは単なる色の変更です。

2)ノードごとに必要な追加情報は1ビットのみであり、多くの場合、無料で取得する方法を見つけることができます。

3)これを頻繁に行う必要はないので、それを行うたびに、もう一度やり直す方法を理解する必要があります。単純なルールと2〜4本の木との対応により、コードは毎回複雑になりますが、毎回簡単に見えるようになります。いつかコードがシンプルになることを願っています。

4)赤黒ツリーが対応する2-4ツリーノードを分割し、色を変更するだけで中間キーを親2-4ノードに挿入する方法は非常にエレガントです。私はそれをするのが大好きです。


0

赤黒またはAVLツリーは、キーが長い場合、または他の何らかの理由でキーの移動に費用がかかる場合、Bツリーなどよりも有利です。

std::setいくつかのパフォーマンス上の理由から、主要なプロジェクト内で独自の代替を作成しました。パフォーマンス上の理由から、赤黒ではなくAVLを選択しました(ただし、この小さなパフォーマンスの強化は、std :: setの代わりに独自にロールする理由ではありませんでした)。「キー」が複雑で移動しにくいことが重要な要因でした。キーの前に別のレベルの間接参照が必要な場合、(a、b)ツリーはまだ意味がありますか?AVLツリーと赤黒ツリーは、キーを移動せずに再構築できるため、キーの移動に費用がかかる場合に利点があります。


皮肉なことに、赤黒木は(a、b)木の「唯一」の特殊なケースであるため、問題はパラメーターの調整にあるように思われますか?(cc @Gilles)
ラファエル
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.