web-dev-qa-db-ja.com

Stackoverflow例外を回避してバイナリツリーで値を検索する

バイナリツリーで値を見つけようとしていて、探している値を持つノードを返しています。

値がツリーの非常に深いレベルにない場合にうまく機能するアルゴリズムを実行しましたが、値が深い位置にある場合、Java.lang.StackOverflowError。これが私のコードです:

class Node {

    // keep these​​​​​​​​‌‌‌‌‌​​‌‌​​​​​​‌​‌​‌‌‌​ fields
    Node left, right;
    int value;

    public Node find(int v){
        if(v > this.value && this.right != null)
            return right.find(v);
        if(v < this.value && this.left != null)
            return left.find(v);
        if(this.value == v)
            return this;
        return null;
    }
}

誰でも私にこの問題の解決策を提案できますか?.

10
Meshredded

最も単純なアプローチは、これをwhileループに変換することです。これは、「テストしている現在のノード」の状態を維持するだけです。

ループの反復ごとに、3つの可能性があります。

  • 現在のノードには適切な値があり、その場合はそれを返すことができます
  • 現在のノードには正しい「サイド」にサブノードがあります。その場合、そのサブノードを新しい「現在のノード」として繰り返し処理を続行できます。
  • 上記はどちらも当てはまりません。その場合、値が見つからず、nullを返すことができます。

だから次のようなもの:

public Node find(int v) {
    Node current = this;
    while (current != null) {
        if (current.value == v) {
            return current;
        }
        // This will drop out of the loop naturally if there's no appropriate subnode
        current = v < current.value ? current.left : current.right;
    }
    return null;
}

またはさらに少ないコードで、おそらくおそらく読みにくくなります:

public Node find(int v) {
    Node current = this;
    // Keep navigating down the tree until either we've run
    // out of nodes to look at, or we've found the right value.
    while (current != null && current.value != v) {
        current = v < current.value ? current.left : current.right;
    }
    return current;
}
14
Jon Skeet

反復としてリキャストしたコードの例:

class Node {

    // keep these​​​​​​​​‌‌‌‌‌​​‌‌​​​​​​‌​‌​‌‌‌​ fields
    Node left, right;
    int value;

    public Node find(int v){
        Node n = this;

        while (n != null)
        {
            if (v > n.value)
                n = n.right;
            else if (v < n.value)
                n = n.left;
            else // v == n.value
                return n;
        }

        return null;
    }
}

編集:不明な場合に備えて、これがどのように機能するかについてのメモ。現在のノードに到達した方法について何も覚えておく必要はないので、検索する必要がある現在のサブツリーのルートのみを追跡します。各ステップで、検索するサブツリーが残っていない(最初の条件)、左側または右側にサブツリーがある(中央の2つの条件)、または実際にルートのルートで値を見つけたと判断しました現在のサブツリー(最後の条件)。サブツリーが不足するまで(while条件)、探し続けます。不足した場合は、値がツリー内にないことがわかり、nullを返します。

編集:コメントで指摘されているように、連続するifsの使用は問題です。 if/else if/elseを使用するようにコードを更新しました。

5
Patrick87

ツリー検索は、大規模な配列の反復を回避するために使用されます。

ツリーアプローチの弱点は、ノード値が順序付けられることです。ツリーが読み込まれると、すべてのノードが左または右に移動し、多くの再帰が発生します。そうは言っても、スタックオーバーフローは多くの再帰を必要とします。

ツリーのバランスを取る傾向がある値をハッシュするか、特定のブランチが長くなりすぎた場合にツリーのバランスをとるようにツリー構築アルゴリズムを拡張できます。

とはいえ、スタックオーバーフローを引き起こすのに十分な数のノードがツリー内にあることも確認する必要があります。ここに示されていないコードにバグがある可能性があります。

0
Steve11235