J'ai donc cherché à mettre en œuvre un algorithme d'ancêtres communs le plus bas. J'ai examiné de nombreux algorithmes différents (principalement des variations de la solution ou des variations de Trajan de la RMQ).
J'utilise un arbre non binaire. Mon arbre changera souvent entre les requêtes et donc le pré-traitement ne serait pas nécessairement utile. L'arbre ne doit pas avoir plus de 50-75 nœuds. Ce que je me demande, c'est si je devrais vous soucier d'utiliser leurs algorithmes ou de rester avec moi-même. p>
mon algorithme p>
6 Réponses :
Pour un arbre qui petit, je ne me dérangerais pas de mettre en œuvre quelque chose de plus complexe. Votre solution a l'air bien, bien que la complexité temporelle soit carrée en termes de hauteur de l'arbre. Si vous pouvez facilement implémenter un En outre, cet algorithme suppose qu'un nœud peut être son propre ancêtre. Sinon, vous devriez légèrement modifier l'algorithme. Considérez cet exemple, p> lorsque vous essayez de trouver l'ancêtre commun le plus bas de B, etc., cet algorithme signalerait B, qui peut ou non être vrai en fonction de la façon dont vous Définir l'ancêtre. P> p> définir code> (la plupart des langues l'ont intégrée), alors l'algorithme pourrait être modifié,
sans regarder les détails de l'un ou l'autre des algorithmes, je suggère de regarder à quel point l'efficacité de ces algorithmes est importante de votre application globale et de la manière dont l'effort serait nécessaire pour mettre en œuvre un autre algorithme. P>
Combien de fois cet algorithme sera-t-il exécuté dans le fonctionnement normal (ou stressé) de votre application? Cela fera-t-il attendre l'utilisateur plus longtemps que nécessaire? Les autres algorithmes d'un ordre de grandeur différent sont-ils plus rapidement que ceux? (Une personne familière avec les algorithmes peut vous donner une réponse plus détaillée à ce sujet.) P>
Je pense qu'il ne vaut pas la peine d'optimiser un peu de code, à moins que vous ne voyiez que des résultats importants (certaines personnes se sentent fortement si une optamisation prématurée est la racine de tout mal ) p>
Votre algorithme est quadratique, mais il peut facilement être fabriqué linéaire. P>
Utilisez simplement HASHTABLE (I.E. SET) pour ParentNode code>, au lieu de la liste.
Vérification donc si un nœud est dans
parentnode code> sera
o (1) code> au lieu de
o (n) p>. P>.
Comme d'autres personnes ont mentionné, votre algorithme est actuellement quadratique. Cela peut ne pas être un problème pour un jeu de données aussi petit que 50-75 nœuds, mais dans tous les cas, il est simple de la modifier en temps linéaire sans utiliser de jeux ni de hashtables, juste en enregistrant le chemin complet de la racine pour chaque nœud, puis En descendant de la racine et à la recherche du premier nœud différent. Le nœud précédent (qui est le parent commun de ces 2 nœuds différents) est alors la LCA: edit 27/5/2012: strong> gérer le cas dans Lequel un nœud est descendu de l'autre, qui autrement entraînerait une tentative de pop () code> une pile vide. Merci à damné de pointer cela. (J'ai également compris qu'il suffit de suivre un seul
oldnode code>.) P> p>
Modification: dans des langues comme Java, POP () peut lancer ELTOLSTACKEXCEPTION, qui devrait être traitée par une manipulation d'exception ou en vérifiant les tailles des deux piles avant de sauter.
@Damned: En réalité, ce n'est pas nécessaire, car le fait que nœud1 code> et
nœud2 code> appartiennent au même arborescence garantit qu'un ancêtre commun sera trouvé dans le dernier
pendant < / Code> Boucle - Aucun de ces appels vers
POP () code> peut éventuellement échouer.
L'exception se produira toujours si nous avons un nœud pour être le descendant de l'autre. Prenez une paire parent-enfant par exemple.
@damned: Vous avez tout à fait raison, mes excuses. Je vais mettre à jour dans un moment pour résoudre ce problème.
Néanmoins, elle ne manipule pas correctement pour un nœud est descendue d'autre que je pense. Say List1 a 3 éléments et liste2 a 2 éléments. Après avoir fonctionné pendant deux fois, cela a un problème.
@NRK: En supposant que vous signiez le cas lorsque nœud1 code> est un enfant de
node2 code>: Ce cas semble fonctionner pour moi lorsque j'écris les états après chaque étape. Le
tandis que code> est testé 3 fois: la première fois,
nœud1 code> et
nœud2 code> sont les deux
null code>, la deuxième fois Ils sont tous deux égaux au nœud racine, la troisième fois qu'ils sont égaux au 2e nœud du chemin de la racine (ce sera
nœud2 code>) mais
parentnode2 code> est vide donc les sorties de la boucle et
si (nœud1 == nœud2) renvoyer le nœud1 code> réussit. Notez que
nœud1 code> est modifié à l'intérieur de la fonction - il ne retournera pas la valeur d'origine transmise.
Je viens d'écrire un article de blog sur la manière dont je devais mettre en œuvre mon propre algorithme pour ce problème, mais étendu à un ensemble de nœuds d'une longueur arbitraire. Vous pouvez le trouver ici (avec une explication graphique étape par étape de la façon dont cela fonctionne) p>
Le lien direct ne fonctionne pas pour moi, cependant, si vous êtes Google, vous y arriverez.
J'ai une solution simpliste trier les deux éléments et le plus bas être laissé et le plus élevé est juste visiter la racine DEF RECURSE (racine) retourner nil si root.empty? Si laissé <= root && droite> = racine racine de retour Elsif quitte <= root && droite <= root recueil (root.left) autre recueil (racine.right) fin p>
Donc, cela va vérifier chaque traversé Le problème soumis à O (log n) temps pour la moyenne et la pire et o (journal p>
Hmm, je ne vois pas comment c'est une vraie question. Quel type de réponse attendez-vous à recevoir?