なぜ、この共通の祖先のソリューションは、より良い最悪の場合のパフォーマンスを持っているのですか?

AfterWorkGuinness:

私は、バイナリツリー(必ずしもバイナリ検索ツリー)内の2つのノードの第1の共通の祖先を見つけるために2つのソリューションで探しています。私は2番目のソリューションは、より良い最悪の場合の実行時間を提供して言われてきたが、私は理由を把握することはできません。誰かが私を啓発してくださいことはできますか?

解決策1:

  • 二つのノードのそれぞれの深さ検索:pは、Q
  • その深さのデルタを計算
  • 浅いノードAのポインタAより深いノードにポインタを設定します
  • 私たちは同じ高さから横断を開始することができますので、デルタによって、より深いノードポインタを上に移動
  • 我々は最初の共通の祖先出ている同じノードに到着するまで、再帰的に両方のポインタの一部のノードを訪問
import com.foo.graphstrees.BinaryTreeNodeWithParent;

/*
   Find the first common ancestor to 2 nodes in a binary tree.
*/
public class FirstCommonAncestorFinder {

    public BinaryTreeNodeWithParent find(BinaryTreeNodeWithParent p, BinaryTreeNodeWithParent q) {

        int delta = depth(p) - depth(q);
        BinaryTreeNodeWithParent first = delta > 0 ? q: p; // use shallower node
        BinaryTreeNodeWithParent second = delta > 0 ? p: q; //use deeper

        second = goUp(second, delta); // move up so they are level, if 1 node is deeper in the tree than the other, their common ancestor obviously cannot be below the shallower node, so we start them off at the same height in the tree


        //keep going up the tree, once first == second, stop
        while(!first.equals(second) && first !=null && second !=null) {
            first = first.getParent();
            second = second.getParent();
        }

        return first == null || second == null ? null : first;

    }

    private int depth(BinaryTreeNodeWithParent n) {
        int depth = 0;
        while (n != null) {
            n = n.getParent();
            depth++;
        }
        return depth;
    }

    private BinaryTreeNodeWithParent goUp(BinaryTreeNodeWithParent node, int delta) {

        while (delta > 0 && node != null) {
            node = node.getParent();
            delta--;
        }
        return node;
    }
}

解決策2:

  • ルートノードから始まるツリー内の両方のノード(P、Q)が存在を確認
  • qはPの子ではなく、pはそのサブツリーを横断することによって、Qの子ではないことを確認します
  • qが見つかるまで再帰的にPの連続した親ノードのサブツリーを調べます
import com.foo.graphstrees.BinaryTreeNodeWithParent;

public class FirstCommonAncestorImproved {

    public BinaryTreeNodeWithParent find(BinaryTreeNodeWithParent root,
                                         BinaryTreeNodeWithParent a,
                                         BinaryTreeNodeWithParent b) {

        if (!covers(root, a) || !covers(root, b)) {
            return null;
        } else if (covers(a, b)) {
            return a;
        } else if (covers(b, a)) {
            return b;
        }

        var sibling = getSibling(a);
        var parent = a.getParent();

        while (!covers(sibling, b)) {
            sibling = getSibling(parent);
            parent = parent.getParent();
        }
        return parent;
    }

    private BinaryTreeNodeWithParent getSibling(BinaryTreeNodeWithParent node) {
        if (node == null || node.getParent() == null) return null;
        var parent = node.getParent();
        return node.equals(parent.getLeft()) ? node.getRight() : node.getLeft();
    }

    private boolean covers(BinaryTreeNodeWithParent root,
                           BinaryTreeNodeWithParent node) {

        if (root == null) return false;
        if (root.equals(node)) return true;
        return covers(root.getLeft(), node) || covers(root.getRight(), node);

    }
}

ジーン:

それは、問題の構造に依存します。

開始ノードは、深い大きな木である場合は、先祖が近くにあり、その後、最初のアルゴリズムは、まだ深さを見つけるために、ルートにパス全体を横断する必要があります。第二は、わずかなサブツリーを調べることによって成功します。

ノードは、深され、共通の祖先は非常に根の近くにある場合には、第2のツリー全体を探索するかもしれない一方、最初の意志だけで、ルートに二つの経路を横切ります。

しばしばそうであるように、あなたがスピードのためのスペースを取引することにより漸近的に高速なアルゴリズムを得ることができることに注意してください。ノードのセットを維持します。トラバース上向きに、両方の出発ノードからのステップを交互あなたがそこにすでにだものを見つけるまで、セットに追加しました。それは共通の祖先です。セット所定の操作はO(1)であり、このアルゴリズムは、kが共通の祖先から最も離れた開始ノードへの経路長であり、O(K)です。あなたは良い結果を出すことはできません。

Set<Node> visited = new HashSet<>();
while (a != null && b != null) {
  if (visited.contains(a)) return a;
  if (visited.contains(b)) return b;
  visited.add(a);
  visited.add(b);
  a = a.parent();
  b = b.parent();
}
while (a != null) {
  if (visited.contains(a)) return a;
  a = a.parent();
}
while (b != null) {
  if (visited.contains(b)) return b;
  b = b.parent();
}
return null;

おすすめ

転載: http://43.154.161.224:23101/article/api/json?id=228884&siteId=1