昔は、データ構造のクラスで、AVLツリーの仕組みを学びました。私はクラスの1つでそれを持っていたでしょうが、インストラクターは「実際にこれを使用することは決してないだろう」と言い、代わりに2〜3本の木とb *木を代わりに学習させました。これらは、メモリが不足し、プロセスが単独でスレッド化されていた日でした。単一リンクリストが同様に機能する場合は、両端キューは使用しませんでした。
現在、スキップリストはより一般的であり、利用可能なメモリと同時実行性が問題になっています(スキップリストでライターとして動作する場合、AVLツリーのすべてと比較して、あまりロックする必要はありません)。
率直に言って、現在の私のお気に入りのデータ構造は、その下でどのように機能し、どこで使用するのが有利か不利かを簡単に推論できるものです。
あなたは最初から書く必要はありません(インタビューの質問としてそれを取得しない限り-しかし、あなたはAVLツリーを実装する可能性があります)。
あなたはされ、選択したい理由を理解するために必要に行くConcurrentSkipListMap
のではなく、JavaでのHashMap
か、TreeMap
あるいは他のマップの実装のいずれか。
仕組みを理解するには、バイナリツリーの仕組みを理解する必要があります。待って、それを修正させてください。平衡二分木がどのように機能するかを理解する必要があります。バイナリツリーのバランスをとらないと、ルックアップで実際の利点が得られません。
このツリーがあるとしましょう:
そして、「8」を挿入します。今、私たちは持っています:
そして、それはバランスが取れていません。だから、私たちはいくつかの実装を介してバランスをとる魔法を行っています...
そして、あなたは再びバランスの取れた木を手に入れました。しかし、それは私が手を振った多くの魔法でした。
スキップリストを見てみましょう。
これはたまたま理想化されたものです。ほとんどありませんが、スキップリストの理想が近似するバランスの取れたバイナリツリーの性質を示しています。
次に、そこに6を挿入します。これは、リンクリストのように挿入されます。ただし、上から始めて下に行きます。上位は5を指しています。6> 5ですか?はい。さて、トップは最後を指していますので、スタックを下っていきます(5番です)。次は7です。6> 7ですか?いや。したがって、レベルを下げて基本レベルにいるので、5の右側に6を挿入します。
私たちはコインを投げます-私たちが作る頭、私たちが残る尾。しっぽ。これ以上何もする必要はありません。
その8を今挿入しましょう。8> 5?うん。8> 7?うん。そして今、私たちは矢印とスタックをたどり、8> 11をテストした後、再び最下層にいますか?いや。したがって、7の右側に8を挿入します。
私たちはコインを投げます-私たちが作る頭、私たちが残る尾。しっぽ。これ以上何もする必要はありません。
バランスの取れたツリーでは、ツリーが現在バランスが取れていないことについてすべての作業が行われます。しかし、これはツリーではありません-そのスキップリストです。バランスのとれたツリーを近似します。
ここで、10を挿入します。すべての比較を避けます。
私たちはコインを投げます-私たちが作る頭、私たちが残る尾。ヘッズ!そして再びそれをひっくり返して、再びヘッズ!もう一度裏返します、OK、尾があります。ベースリンクリストの2レベル上。
ここでの利点は、12を挿入する場合、他のすべての比較を行わずに5から10までスキップできることです。スキップリストでそれらをスキップできます。そして、バランスのとれたツリーについて心配する必要はありません。スタッキングの確率的性質がそれを私たちにもたらします。
なぜこれが便利なのですか?10を挿入するとき、構造全体ではなく、5および7と8のポインターで書き込みをロックすることでそれができるからです。そして、私がそれをしている間、読者は一貫性のない状態にならずにスキップリストを通過することができます。同時使用の場合、ロックする必要がないので高速です。最下層での反復処理は、ツリーよりも高速です(ツリーナビゲーション用のBFSおよびDFSアルゴリズムの楽しさ-それらについて心配する必要はありません)。
それに遭遇しますか?あなたはおそらくそれを場所で使用しているのを見るでしょう。著者が選んだ理由と、あなたは知っているよというのではなく、実装のTreeMap
か、HashMap
構造のため。
これらの多くは私のブログ投稿「The Skip List」から借用しています。