Comment implémenteriez-vous en Java la classe de nœud d'arbre binaire et la classe d'arbre binaire pour prendre en charge la méthode de contrôle d'égalité la plus efficace (du point de vue de l'exécution) (doit également être implémentée):
boolean equal(Node<T> root1, Node<T> root2) {}
ou
boolean equal(Tree t1, Tree t2) {}
Tout d'abord, j'ai créé la classe Node comme suit:
public class Node<T> {
private Node<T> left;
private Node<T> right;
private T data;
// standard getters and setters
}
puis la méthode equals qui prend 2 nœuds racine en tant qu'arguments et exécute la comparaison récursive standard:
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;
}
Ma deuxième tentative a été d'implémenter les arbres en utilisant des tableaux et des index pour la traversée. La comparaison peut ensuite être effectuée à l'aide des opérations au niveau des bits (AND) sur deux tableaux: lisez le bloc de 2 tableaux et masquez-les l'un après l'autre à l'aide de l'opérateur AND logique. Je n’ai pas réussi à faire fonctionner mon code, je ne l’affiche donc pas ici (je vous serais reconnaissant de mettre en œuvre la deuxième idée ainsi que vos améliorations).
Avez-vous des idées sur la manière de réaliser le test d’égalité des arbres binaires le plus efficacement possible?
MODIFIER
La question suppose une égalité structurelle. (Pas l'égalité sémantique)
Cependant, le code qui teste l’égalité sémantique, par ex. "Faut-il considérer que les deux arbres sont égaux si leur contenu est identique, même si leur structure ne l'est pas?" Il suffirait de parcourir l’arbre dans l’ordre et cela devrait être simple.
Pour une chose, vous vérifiez toujours les branches, même si vous constatez que les racines sont inégales. Votre code serait plus simple (IMO) et plus efficace si vous veniez de retourner false
dès que vous constatiez une inégalité.
Une autre option pour simplifier les choses consiste à autoriser votre méthode equals
à accepter les valeurs null
et à comparer deux valeurs NULL égales. De cette façon, vous pouvez éviter tous ces contrôles de nullité dans les différentes branches. Cela ne le rendra pas plus efficace, mais ce sera plus simple:
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());
}
Notez qu'actuellement, cela échouera si root1.getData()
renvoie null
. (Cela peut être impossible avec la façon dont vous ajoutez des nœuds.)
EDIT: Comme indiqué dans les commentaires, vous pouvez utiliser des codes de hachage pour créer un "départ rapide" très rapide - mais cela ajouterait de la complexité.
Soit vous devez rendre vos arbres immuables (ce qui est une toute autre discussion) ou il faut que chaque nœud connaisse son parent, de sorte que lorsque le nœud est changé (par exemple en ajoutant une feuille ou en modifiant la valeur), il doit mettre à jour son code de hachage et lui demander parent à mettre à jour aussi .
Par curiosité, estimez-vous que les deux arbres sont égaux si leur contenu est identique, même si leur structure ne l’est pas? Par exemple, sont-ils égaux?
B C C A
/ \ / \ / \ \
A D B D A D B
/ / \ \
C A B C
\
D
Ces arbres ont le même contenu dans le même ordre, mais étant donné que les structures sont différentes, vos tests ne seraient pas égaux.
Si vous voulez tester cette égalité, personnellement, je construirais simplement un itérateur pour l'arbre en utilisant le parcours dans l'ordre et l'itération entre les arbres en les comparant élément par élément.
Tout d’abord, je fais quelques hypothèses générales. Ce sont des hypothèses valables pour la plupart des classes de collection basées sur des arbres, mais cela vaut toujours la peine de vérifier:
En supposant que ces hypothèses soient correctes, l'approche que je suggérerais est la suivante:
this
ne peut pas être null). En outre, la machine virtuelle Java est très efficace pour optimiser les méthodes statiques.Mon implémentation serait donc quelque chose comme:
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;
}
Pour tout arbre, le moyen le plus efficace de le représenter pour que vous puissiez facilement vérifier l’égalité est la liste parente - conservez un tableau dans lequel, pour chaque sommet, vous vous souvenez de l’index de son parent (en fait, une paire - l’index du père et la valeur de données). Ensuite, vous devriez simplement faire une comparaison de deux blocs de mémoire continus.
Cela ne fonctionnera que si l’arbre est statique (c’est-à-dire que cela ne change pas dans le temps). De plus, les arbres ne seront égaux que si les index des sommets sont les mêmes dans les deux arbres.
Je crois que dans le cas habituel où les deux déclarations ci-dessus ne sont pas vraies, votre mise en œuvre devrait être aussi rapide que possible.
EDIT: en fait, votre mise en oeuvre peut être améliorée si vous suivez les conseils de la réponse de Jon Skeet (au moins, retournez false dès que vous savez que les arbres ne sont pas égaux).
Le code donné ci-dessus retournerait vrai pour deux arbres inégaux avec les mêmes valeurs racine Je ne pense pas que c'est ce que tu veux. Ça ne devrait pas être:
si (! a == b) renvoie false;
De cette façon, la méthode effectuerait le reste des vérifications.
(Impossible de se connecter d'ici pour une raison quelconque.)