Imaginez un graphe acyclique dirigé comme suit:
Quel algorithme pourrais-je utiliser pour déterminer l'ancêtre commun le plus bas (ACL) de deux nœuds arbitraires, par exemple, l'ancêtre commun de:
Remarque:
Le lien de Den Roman semble prometteur, mais cela me semblait un peu compliqué, alors j'ai essayé une autre approche. Voici un algorithme simple que j'ai utilisé:
Supposons que vous vouliez calculer l'ACL (x, y) avec x et y deux nœuds . Chaque nœud doit avoir une valeur color
et count
, resp. initialisé à white et 0.
count
de ses parents d'une unité.Chaque nœud red ayant une valeur count
définie sur 0 est une solution.
Il peut y avoir plus d'une solution, selon votre graphique. Par exemple, considérons ce graphique:
LCA (4,5) solutions possibles sont 1 et 2.
Notez que cela fonctionne toujours si vous voulez trouver l’ACV de 3 nœuds ou plus, il vous suffit d’ajouter une couleur différente pour chacun d’eux.
Je cherchais une solution au même problème et j'ai trouvé une solution dans l'article suivant:
http://dx.doi.org/10.1016/j.ipl.2010.02.014
En bref, vous ne recherchez pas l’ancêtre commun le plus bas, mais l’ancêtre commun UNIQUE le plus bas, qu’ils définissent dans le présent document.
Juste une pensée sauvage. Pourquoi ne pas utiliser les deux nœuds d’entrée en tant que racines et exécuter deux BFS simultanément, étape par étape? À une certaine étape, lorsque leurs ensembles BLACK se chevauchent (enregistrement des nœuds visités), l'algorithme s'arrête et les nœuds qui se chevauchent sont leur (s) ACL. De cette manière, tous les ancêtres communs auront des distances plus longues que celles que nous avons découvertes.
Je sais que c'est une vieille question et une assez bonne discussion, mais comme j'avais un problème similaire à résoudre, je suis tombé sur JGraphT 's Plus bas ancêtre commun algorithmes, j'ai pensé que cela pourrait aider:
Si le graphique a des cycles, alors «ancêtre» est défini de manière vague. Peut-être que vous voulez parler de l'ancêtre sur la sortie de l'arborescence d'un DFS ou d'un BFS? Ou peut-être par «ancêtre», vous voulez dire le nœud du digraphe qui minimise le nombre de sauts de E
et B
?
Si vous n'êtes pas inquiet pour la complexité, vous pouvez alors calculer un A * (ou le plus court chemin de Dijkstra) de chaque nœud à la fois pour E
et B
. Pour les nœuds pouvant atteindre E
et B
, vous pouvez trouver le nœud qui minimise PathLengthToE + PathLengthToB
.
EDIT: Maintenant que vous avez clarifié quelques points, je pense que je comprends ce que vous recherchez.
Si vous ne pouvez que "monter" dans l’arbre, alors je vous suggère d’effectuer un BFS à partir de E
et également un BFS à partir de B
. Chaque variable de votre graphique est associée à deux variables: les sauts de B
et les sauts de E
. Laissez B
et E
des copies de la liste des noeuds de graphe. La liste de B
est triée par Hops from B
tandis que la liste de E
est triée par Hops from E
.
Pour chaque élément de la liste B
, essayez de le trouver dans la liste E
. Placez les correspondances dans une troisième liste, triées par nombre de sauts de B
+ Houblons de E
. Une fois que vous avez épuisé la liste de B
, votre troisième liste triée doit contenir la LCA en tête. Cela permet une solution, plusieurs solutions (choisies arbitrairement parmi leur ordre de BFS pour B
) ou aucune solution.
http://www.gghh.name/dibtp/2014/02/25/how-does-Mercurial-select-the-greatest-common-ancestor.html
Ce lien décrit la procédure à suivre dans Mercurial. L’idée de base est de rechercher tous les parents pour les nœuds spécifiés, de les regrouper par distance à partir de la racine, puis de rechercher ces groupes.
Supposons que vous souhaitiez trouver les ancêtres de x et y dans un graphique.
Maintenir un tableau de vecteurs - parents (stockage des parents de chaque noeud).
Tout d'abord, faites un bfs (gardez les parents de chaque sommet) et trouvez tous les ancêtres de x (trouvez les parents de x et utilisez parents , trouvez tous les ancêtres de x) et stockez-les dans un vecteur. Enregistrez également la profondeur de chaque parent dans le vecteur.
Trouvez les ancêtres de y en utilisant la même méthode et stockez-les dans un autre vecteur. Maintenant, vous avez deux vecteurs stockant les ancêtres de x et y respectivement avec leur profondeur.
ACV serait un ancêtre commun avec la plus grande profondeur. La profondeur est définie comme la plus longue distance de la racine (sommet avec in_degree = 0). Nous pouvons maintenant trier les vecteurs par ordre décroissant de profondeur et déterminer l’ACV. En utilisant cette méthode, nous pouvons même trouver plusieurs ACV (le cas échéant).
J'ai également besoin de la même chose, trouver l'ACL dans un DAG (graphe acyclique dirigé). Le problème LCA est lié à RMQ (Range Minimum Query Problem).
Il est possible de réduire l'ACV à RMQ et de trouver l'ACV souhaitée de deux nœuds arbitraires à partir d'un graphe acyclique dirigé.
J'ai trouvé CE TUTORIAL détail et bien. Je prévois également de mettre cela en œuvre.
Je propose la solution de complexité temporelle O (| V | + | E |), et je pense que cette approche est correcte, sinon corrigez-moi.
Étant donné le graphe acyclique dirigé, nous devons trouver une ACV de deux sommets v et w.
Étape 1: recherchez la distance la plus courte entre tous les sommets du sommet racine à l’aide de bfs http://en.wikipedia.org/wiki/Breadth-first_search avec une complexité temporelle O (| V | + | E |) et recherchez également le parent de chaque sommets.
Étape 2: Recherchez les ancêtres communs des deux sommets en utilisant le parent jusqu'à atteindre le sommet de la racine. Complexité temporelle- 2 | v |
Étape 3: L'ACV sera cet ancêtre commun qui aura la distance maximale la plus courte.
Donc, c’est l’algorithme de complexité temporelle O (| V | + | E |).
S'il vous plaît, corrigez-moi si je me trompe ou si d'autres suggestions sont les bienvenues.