- バイナリ検索ツリー
- 求めます
- 挿入
- 演奏
- 秩序関連の操作
- 最大のキー、最小キー
- 、切り上げ切り捨て
- セレクション、ランキング
- 範囲を探します
- 削除
- キーの最大、最小のキーを削除します。
- 一般的な削除
バイナリ検索ツリー
フロント無秩序の理解とパフォーマンスのリニアレベルの観点から、少なくともリンクリストの配列を命じたが、大規模なデータアプリケーションに使用することはできません。バイナリ検索ツリーを学ぶために次はコンピュータサイエンスを組み合わせるために探しているの秩序配列の柔軟性と効率性のリストに挿入することができますすることは最も重要なアルゴリズムの一つです。
バイナリ検索ツリー(バイナリ検索ツリー)は、各ノードが同等のキーが含まれた二分木であり、各ノードのキーに関連付けられた値は、任意のノードの左サブツリーよりも大きいですキーは、キーは、任意のノードの右部分木よりも小さくなっています。
求めます
場合は、バイナリ検索ツリー検索ツリーが空の場合は、その後、ミスを検索し、キーと、ルートキーを見つけることが等しい場合、ヒットを見つけるために、それを見つけるのであれば、それ以外の場合は、再帰的にサブツリーで、検索し続け、キーは、左のサブツリーを選択するか、右のサブツリーを選択し、ルート未満です。
:として実装コードの検索アルゴリズム
public class BST<Key extends Comparable<Key>, Value> {
private Node root;
private class Node {
private Key key;
private Value val;
private Node left, right;
public int size;
public Node(Key key, Value val, int size) {
this.key = key;
this.val = val;
this.size = size;
}
}
public Value get(Key key) {
return get(root, key);
}
private Value get(Node x, Key key) {
if (key == null)
throw new IllegalArgumentException("calls get() with a null key");
if (x == null)
return null;
int cmp = key.compareTo(x.key);
if (cmp > 0) {
return get(x.right, key);
} else if (cmp < 0) {
return get(x.left, key);
} else {
return x.val;
}
}
}
、ノードクラスは、ノードのバイナリ検索ツリーを表すために使用され、各ノードが使用可能なキー、値、及び後部左右のリンクの最大最小値を含む場合順序付け操作のノードのカウンタ。
挿入
キーの挿入時に、見つけることが最初、キーが既に対応する値を更新し、シンボルテーブルに存在する場合、ルックアップ・ミスの場合、キーと値のペアを含む新しいノードを挿入することに戻ります。
public void put(Key key, Value val) {
root = put(root, key, val);
}
private Node put(Node x, Key key, Value val) {
if (key == null)
throw new IllegalArgumentException("calls put() with a null key");
if (x == null)
return new Node(key, val, 1);
int cmp = key.compareTo(x.key);
if (cmp < 0)
x.left = put(x.left, key, val);
else if (cmp > 0)
x.right = put(x.right, key, val);
else {
x.val = val;
}
x.size = size(x.left) + size(x.right) + 1;
return x;
}
挿入オペレーションコード同様の検索が、ノードの更新を挿入したり、新しいノード値を追加し、ノードのカウンタを更新します。
x.left = PUT(x.left、キー、ヴァル); 単純な再帰実現添加整定ポイントの特性に類似したコード。再帰呼び出しは、見つけるために同等のバイナリのロジックによると、木に沿って枝を見下している場合が見つかった場合、再帰の終了時に、更新ノードの値は、下の木は、この時点で、見つからなかった場合キー== nullの確立、再帰が終了すると、新しいノードの初期化はx.leftまたはx.rightにぶら下がってきました。
等価の再帰手順の放出は、木に沿って各レベルを登る、登る; x.size =サイズ(x.left)+サイズ(x.right)+ 1が実行された後、そのように追加ノード関連するパスのサイズ上のすべてのノードが更新されました。
演奏
木の形状は、木の深さを決定するために、新しいノードを挿入することは、ツリー全体枚のルートノードからツリーの下なので、木の二分探索木の形状関連のプロパティを見つけるために検索する必要性を欠場します。最良のケースでは、ノードNがルートノードLGNからの距離と完全にバランスのとれたツリー、底空間内のすべてのリンクである含み、そして最悪の場合には、ツリーの形状となりますツリーの深さは要素の順に選択Nは、バイナリ検索ツリーに挿入されているリストになって、それが問題を引き起こす可能性があります。一般的な場合に、最良の場合、対数レベルでバイナリ検索ツリーの性能に近い得られた木の形状。
14350文字の合計で英語のオリジナルの単語「物語」7より大きい、これらの言葉は、次のような結果を達成するために、シンボルテーブルの異なる特性をテストするためのキーとして5737個の異なる単語、これらの単語を持っています:
横軸は、挿入された単語の数を表し、縦軸は挿入時の比較の数を表し、実際のグレー点は比較挿入の特定の数を表し、赤い点は、比較の平均数を表し(合計を比較する/単語の数を挿入する)ことなく、上記に基づいて研究さそれぞれ、リンクリストと2246年の平均数と484回の規則的な配列を達成するためには、二分探索木の観点で見ることができる回数に単語または側面の平均数を比較し、前方の大きさの飛躍の順序があります。
秩序関連の操作
二分探索木に加えて、優れた性能を持っていますが、また、その秩序のと順序に関連するキー操作を維持するためにサポートしています。
最大のキー、最小キー
サブツリーが空のままにされている場合、ノードの左の部分木の値が右サブツリーより小さく、最小値は、左の部分木であってもよく、その後、現在のノードが最小です。最大値を選択する派生このアルゴリズムに基づいて、コードの最小値が実現されます。
public Key min() {
if (isEmpty())
throw new NoSuchElementException("calls min() with empty symbol table");
return min(root).key;
}
private Node min(Node x) {
if (x.left == null)
return x;
else
return min(x.left);
}
public Key max() {
if (isEmpty())
throw new NoSuchElementException("calls max() with empty symbol table");
return max(root).key;
}
private Node max(Node x) {
if (x.right == null)
return x;
else
return max(x.right);
}
、切り上げ切り捨て
指定された鍵が、指定された鍵が、ルートノードよりも大きい場合、ときにのみ、ルート左サブツリーのルートノードよりも少ないし、ルートキー以下である場合、最大のキーは、切り捨て右の部分木のノードがキー所与の現在のノード以下であり、右の部分木に現れる切り捨て値は、ルートノードは値を見つけることであるか、またはこれに類似の方法を切り上げ。
`` `
パブリック床キー(キーキー){
ノード= N-床(ルート、KEY);
IF(N - == NULL){
戻りNULL;
}そうでなければ{
戻りn.key;
}
}
プライベート・ノード床(ノードX、キーのキー){
場合(X == nullの){
リターンNULL;
}
int cmp = key.compareTo(x.key);
if (cmp == 0)
return x;
if (cmp < 0)
return floor(x.left, key);
Node n = floor(x.right, key);
if (n == null) {
return x;
} else {
return n;
}
}
公開鍵天井(主キー){
IF(N == NULL){
戻りヌル。
}他{
n.key返します。
}
}
プライベート・ノード天井(ノードX、キーのキー){
場合(X == nullの){
リターンNULL;
}
int cmp = key.compareTo(x.key);
if (cmp == 0)
return x;
if (cmp > 0)
return ceiling(x.right, key);
Node n = ceiling(x.left, key);
if (n == null) {
return x;
} else {
return n;
}
}
`` `
セレクション、ランキング
0からランク付けされ、SELECT選択方法(k)がランクキーを返し、それに小さなツリーは、k個のキーがあります。ノードtの左部分木がkよりも大きい場合、それはtがkと等しい場合は、左のサブツリーで検索し続け、その後、根はtがkよりも小さい場合、右のサブツリーにあるランキングを探して、その後、鍵を見つけることですKT-1キー、コードはこのようにして得られます。
public Key select(int k) {
return select(root, k).key;
}
private Node select(Node x, int k) {
if (x == null) {
return null;
}
int t = size(x.left);
if (t > k) {
return select(x.left, k);
} else if (t < k) {
return select(x.right, k - t - 1);
} else {
return x;
}
}
ランクランク()メソッドは、ソート指定したキーに戻り、選択肢逆方法、の方法です。指定されたキーは、ルートノードに等しい場合、順位のルートキーは、左サブツリーTにおけるノードの総数であり、与えられたキーは、ルートノードよりも小さい場合に、再帰的に左サブツリーで算出された、与えられたキー場合右サブツリー内のその位置と共にT + 1を返し、ルートノードよりも大きいです。
public int rank(Key key) {
return rank(key, root);
}
private int rank(Key key, Node x) {
if (x == null) {
return 0;
}
int cmp = key.compareTo(x.key);
if (cmp > 0) {
return size(x.left) + rank(key, x.right) + 1;
} else if (cmp < 0) {
return rank(key, x.left);
} else {
return size(x.left);
}
}
範囲を探します
行きがけ - すべての検索は、与えられた範囲内のキーの範囲に戻るように求め、この基本的な方法は、バイナリツリートラバーサルに使用されます。左サブツリー内のすべてのキーの最初のトラバーサルは、その後、ルートノードを通過し、最後に右サブツリー内のすべてのキーは、このプロセスを再帰的に、それはすべてのノードをトラバース仕上げの昇順にすることができます。
public void keys(Node x, Queue<Key> queue, Key lo, Key hi) {
if (x == null)
return;
int cmplo = lo.compareTo(x.key);
int cmphi = hi.compareTo(x.key);
if (cmplo < 0)
keys(x.left, queue, lo, hi);
if (cmplo <= 0 && cmphi >= 0)
queue.enqueue(x.key);
if (cmphi > 0)
keys(x.right, queue, lo, hi);
}
削除
キーの最大、最小のキーを削除します。
あなたが最小のキーを削除すると、彼女は空のリンク、ノードの右部分木にノードポイントへ、リンクを満たされるまで、あなたは、左の部分木の深い根を維持する必要があり、ノードがいないので削除しますオブジェクト参照は、その後、ガベージコレクタは離れてクリアされます。最大のキープロセスが似て削除します。
public void deleteMin() {
root = deleteMin(root);
}
private Node deleteMin(Node x) {
if (x.left == null)
return x.right;
x.left = deleteMin(x.left);
x.size = size(x.left) + size(x.right) + 1;
return x;
}
public void deleteMax() {
root = deleteMax(root);
}
private Node deleteMax(Node x) {
if (x.right == null)
return x.left;
x.right = deleteMax(x.right);
x.size = size(x.left) + size(x.right) + 1;
return x;
}
時間の左の部分木の深化では、満たさエアリンクない限り、deleteMin(ノードX)メソッドは、ノードx、最後の再帰ノード点x.right前の最後のものだけを返し、再帰的な出口は、パスに更新されますノードカウンター。
一般的な削除
二分探索木の方法は、delete()メソッドを達成し、最大値、最小キー、2つのつの子ノードが削除されたノードを削除するのが最も困難であり、一方だけが空ではないが、一般的に二つのノードが存在します子ノードは、このノードを削除し、我々はその2つの子ノードとの合理的な契約を必要とします。1962年T.Hibbard第一ノードXを削除した後、その位置を埋めるために、その後続ノードでこの問題を解決する方法を提案しました。Xは、右の子ノード、右の部分木の最も要約となるその後継ノード点を有しているからです。x.keyとその後続ノードの間には、他の結合が存在しないため、このような変更は、まだ、木の秩序を保証することができます。この操作を完了し、4つのステップが必要です。
- トンへの削除リンクされようとしている点ノードを保存します。
- ポイントは、その後継ノード分(t.right)をX;
- Xの右リンク(すべてのノードに元々点がx.keyバイナリ検索ツリーよりも大きい)deleteMin(t.right)を指し、つまり、すべてのノードを削除した後、後者の息子二フォークx.key上に残っていますツリーを検索します。
- Xの左リンク(元々空)は(その下のすべてのキーが削除されたノードとその後続ノードよりも小さい)t.leftセット。
public void delete(Key key) {
root = delete(root, key);
}
private Node delete(Node x, Key key) {
if (x == null)
return null;
int cmp = key.compareTo(x.key);
if (cmp < 0)
x.left = delete(x.left, key);
else if (cmp > 0)
x.right = delete(x.right, key);
else {
if (x.right == null)
return x.left;
if (x.left == null)
return x.right;
Node t = x;
x = min(t.right);
x.right = deleteMin(t.right);
x.left = t.left;
}
x.size = size(x.left) + size(x.right) + 1;
return x;
}