Javaバイナリツリーノードクラスとバイナリツリークラスをどのように実装して、(実行時の観点から)最も効率的な等価チェックメソッドをサポートしますか?)
boolean equal(Node<T> root1, Node<T> root2) {}
または
boolean equal(Tree t1, Tree t2) {}
最初に、Nodeクラスを次のように作成しました。
public class Node<T> {
private Node<T> left;
private Node<T> right;
private T data;
// standard getters and setters
}
次に、2つのルートノードを引数として取り、標準の再帰的比較を実行するequalsメソッド:
public boolean equals(Node<T> root1, Node<T> root2) {
boolean rootEqual = false;
boolean lEqual = false;
boolean rEqual = false;
if (root1 != null && root2 != null) {
rootEqual = root1.getData().equals(root2.getData());
if (root1.getLeft()!=null && root2.getLeft() != null) {
// compare the left
lEqual = equals(root1.getLeft(), root2.getLeft());
}
else if (root1.getLeft() == null && root2.getLeft() == null) {
lEqual = true;
}
if (root1.getRight() != null && root2.getRight() != null) {
// compare the right
rEqual = equals(root1.getRight(), root2.getRight());
}
else if (root1.getRight() == null && root2.getRight() == null) {
rEqual = true;
}
return (rootEqual && lEqual && rEqual);
}
return false;
}
私の2番目の試みは、トラバース用の配列とインデックスを使用してツリーを実装することでした。次に、2つの配列のビット単位演算(AND)を使用して比較を実行できます。2つの配列からチャンクを読み取り、論理ANDを使用して1つずつマスクします。私は自分のコードを機能させることができなかったので、ここに投稿しません(2番目のアイデアの実装と改善点に感謝します)。
バイナリツリーの同等性テストを最も効率的に行う方法はありますか?
[〜#〜]編集[〜#〜]
質問は構造的平等を前提としています。 (意味的平等ではない)
ただし、セマンティックの等価性をテストするコード。 「構造が異なっていても、内容が同じであれば2つのツリーは等しいと考えるべきでしょうか?」順番にツリーを反復するだけで、簡単です。
1つには、ルートが等しくないことに気付いたとしても、ブランチをチェックしているalwaysです。不等式を見つけてすぐにfalse
を返した場合、コードはより単純(IMO)で効率的になります。
物事を単純化する別のオプションは、equals
メソッドがnull
値を受け入れ、2つのnullが等しいと比較できるようにすることです。そうすれば、さまざまなブランチでのすべての無効性チェックを回避できます。これによって効率が上がることはありませんが、単純になります。
_public boolean equals(Node<T> root1, Node<T> root2) {
// Shortcut for reference equality; also handles equals(null, null)
if (root1 == root2) {
return true;
}
if (root1 == null || root2 == null) {
return false;
}
return root1.getData().equals(root2.getData()) &&
equals(root1.getLeft(), root2.getLeft()) &&
equals(root1.getRight(), root2.getRight());
}
_
現在、これはroot1.getData()
がnull
を返すと失敗することに注意してください。 (これは、ノードを追加する方法で可能になる場合とできない場合があります。)
編集:コメントで説明したように、ハッシュコードを使用すると、非常にすばやく「早期に」作成できますが、複雑になります。
どちらかあなたはツリーを不変にする必要があります(これは他の議論全体です)or各ノードがその親について知る必要があるため、ノードが変更されると(例えば葉を追加するか、値を変更することで)ハッシュコードを更新する必要がありますそして親にも更新を依頼する。
好奇心から、2つのツリーの構造が異なっていても、内容が同じであれば2つのツリーは等しいと考えますか?たとえば、これらは同じですか?
B C C A
/ \ / \ / \ \
A D B D A D B
/ / \ \
C A B C
\
D
これらのツリーは同じコンテンツを同じ順序で持っていますが、構造が異なるため、テストによっては等しくありません。
この等価性をテストしたい場合は、個人的には、順序トラバーサルを使用してツリーのイテレータを作成し、要素ごとに比較してツリーを反復処理します。
まず、いくつかの一般的な仮定を行います。これらは、ほとんどのツリーベースのコレクションクラスに有効な前提条件ですが、常に確認する価値があります。
これらの仮定が正しいと仮定すると、私が提案するアプローチは次のとおりです。
this
はnullにできないため、インスタンスメソッドには適していません)。さらに、JVMは静的メソッドの最適化に非常に優れています。したがって、私の実装は次のようになります。
public static boolean treeEquals(Node a, Node b) {
// check for reference equality and nulls
if (a == b) return true; // note this picks up case of two nulls
if (a == null) return false;
if (b == null) return false;
// check for data inequality
if (a.data != b.data) {
if ((a.data == null) || (b.data == null)) return false;
if (!(a.data.equals(b.data))) return false;
}
// recursively check branches
if (!treeEquals(a.left, b.left)) return false;
if (!treeEquals(a.right, b.right)) return false;
// we've eliminated all possibilities for non-equality, so trees must be equal
return true;
}
どのツリーでも、等価性を簡単に確認できるようにツリーを表現する最も効率的な方法は、親リストです。各頂点について、その親のインデックスを覚えている配列を保持します(実際にはペアを保持します-親のインデックスとデータ値)。次に、メモリの2つの連続したブロックを比較するだけです。
これは、ツリーが静的である(つまり、時間とともに変化しない)場合にのみ機能します。また、頂点インデックスが2つのツリーで同じである場合にのみ、ツリーが等しいと見なします。
上記の2つのステートメントが当てはまらない場合の一般的なケースは、私は信じています。あなたの実装は、可能な限り速くすべきです。
編集:実際には、ジョン・スキートの回答のアドバイスに従うと、実装が改善される可能性があります(少なくともツリーが等しくないことがわかったらすぐにfalseを返します)
上記のコードは、同じルート値を持つ2つの等しくないツリーに対してtrueを返します。これがあなたの望んでいることだとは思いません。それはすべきではありません:
if(!a == b)はfalseを返します。
このようにして、メソッドは残りのチェックを実行します。
(何らかの理由で、ここからログインできません。)