web-dev-qa-db-fra.com

Explication de l'algorithme pour trouver des points d'articulation ou couper des sommets d'un graphique

J'ai cherché sur le net et je n'ai trouvé aucune explication d'un algorithme DFS pour trouver tous les sommets d'articulation d'un graphique. Il n'y a même pas de page wiki.

En lisant autour, j'ai appris les faits de base d'ici. PDF

Il y a une variable à chaque nœud qui regarde réellement les bords arrière et trouve le nœud le plus proche et le plus haut vers le nœud racine. Après avoir traité tous les bords, il serait trouvé.

Mais je ne comprends pas comment trouver cette variable down & up sur chaque nœud lors de l'exécution de DFS. Que fait exactement cette variable?

Veuillez expliquer l'algorithme.

Merci.

26
Ashish Negi

La recherche de sommets d'articulation est une application de DFS.

En un mot,

  1. Appliquer DFS sur un graphique. Obtenez l'arborescence DFS.
  2. Un nœud qui est visité plus tôt est un "parent" des nœuds qui sont atteints par lui et visités plus tard.
  3. Si un enfant d'un nœud n'a pas de chemin vers l'un des ancêtres de son parent, cela signifie que la suppression de ce nœud rendrait cet enfant disjoint du graphique.
  4. Il y a une exception: la racine de l'arbre. S'il a plus d'un enfant, alors c'est un point d'articulation, sinon non.

Le point 3 signifie essentiellement que ce nœud est un point d'articulation.

Maintenant, pour un enfant, ce chemin vers les ancêtres du nœud serait à travers un back-Edge à partir de lui ou de l'un de ses enfants.

Tout cela est magnifiquement expliqué dans ce PDF .

38
Ashish Negi

J'essaierai de développer une compréhension intuitive du fonctionnement de cet algorithme et donnerai également un pseudocode commenté qui génère des bi-composants ainsi que des ponts.

Il est en fait facile de développer un algorithme de force brute pour les points d'articulation. Retirez simplement un sommet et exécutez BFS ou DFS sur un graphique. S'il reste connecté, alors le sommet n'est pas un point d'articulation, sinon il l'est. Cela s'exécutera dans O(V(E+V)) = O(EV) temps. Le défi est de savoir comment le faire en temps linéaire (c'est-à-dire O(E+V)).

Les points d'articulation connectent deux (ou plus) sous-graphiques. Cela signifie qu'il n'y a pas de bords d'un sous-graphique à l'autre. Imaginez donc que vous êtes dans l'un de ces sous-graphiques et que vous visitez son nœud. Lorsque vous visitez le nœud, vous le marquez, puis passez au nœud suivant sans drapeau en utilisant certains Edge disponibles. Pendant que vous faites cela, comment savez-vous que vous êtes toujours dans le même sous-graphique? L'idée ici est que si vous êtes dans le même sous-graphe, vous verrez finalement un nœud marqué à travers un Edge tout en visitant un nœud non marqué. Cela s'appelle un Edge arrière et indique que vous avez un cycle. Dès que vous trouvez un Edge arrière, vous pouvez être sûr que tous les nœuds passant par ce nœud marqué à celui que vous visitez en ce moment font tous partie du même sous-graphique et qu'il n'y a pas de points d'articulation entre les deux. Si vous n'avez vu aucun bord arrière, tous les nœuds que vous avez visités jusqu'à présent sont tous des points d'articulation.

Nous avons donc besoin d'un algorithme qui visite les sommets et marque tous les points entre la cible des bords arrière comme actuellement en cours de visite nœuds comme dans le même sous-graphique. Il peut évidemment y avoir des sous-graphiques dans les sous-graphiques, nous devons donc sélectionner le plus grand sous-graphique que nous avons jusqu'à présent. Ces sous-graphiques sont appelés bi-composants. Nous pouvons implémenter cet algorithme en attribuant à chaque bi-composant un ID qui est initialisé comme juste un décompte du nombre de sommets que nous avons visités jusqu'à présent. Plus tard, lorsque nous trouverons des bords arrière, nous pouvons réinitialiser l'ID bicomposé au plus bas que nous ayons trouvé jusqu'à présent.

Nous avons évidemment besoin de deux passes. Dans la première passe, nous voulons déterminer quel sommet nous pouvons voir de chaque sommet à travers les bords arrière, le cas échéant. Dans la deuxième passe, nous voulons visiter les sommets dans la direction opposée et collecter l'ID minimal à deux composants (c'est-à-dire l'ancêtre le plus ancien accessible à tous les descendants). DFS s'inscrit naturellement ici. Dans DFS, nous descendons d'abord puis remontons pour que les deux passes ci-dessus puissent être effectuées en une seule traversée DFS.

Maintenant sans plus tarder, voici le pseudocode:

time = 0
visited[i] = false for all i
GetArticulationPoints(u)
    visited[u] = true
    u.st = time++
    u.low = v.st    //keeps track of highest ancestor reachable from any descendants
    dfsChild = 0    //needed because if no child then removing this node doesn't decompose graph
    for each ni in adj[i]
        if not visited[ni]
            GetArticulationPoints(ni)
            ++dfsChild
            parents[ni] = u
            u.low = Min(u.low, ni.low)  //while coming back up, get the lowest reachable ancestor from descendants
        else if ni <> parent[u] //while going down, note down the back edges
            u.low = Min(u.low, ni.st)

    //For dfs root node, we can't mark it as articulation point because 
    //disconnecting it may not decompose graph. So we have extra check just for root node.
    if (u.low = u.st and dfsChild > 0 and parent[u] != null) or (parent[u] = null and dfsChild > 1)
        Output u as articulation point
        Output edges of u with v.low >= u.low as bridges
    output u.low as bicomponent ID
13
Shital Shah

Si low du descendant de u est supérieur à dfsnum de u, alors u est dit être le point d'articulation.

int adjMatrix[256][256];
int low[256], num=0, dfsnum[256];

void cutvertex(int u){
    low[u]=dfsnum[u]=num++;
    for (int v = 0; v < 256; ++v)
    {
        if(adjMatrix[u][v] && dfsnum[v]==-1)
        {
            cutvertex(v);
            if(low[v]>dfsnum[u])    
                cout<<"Cut Vertex: "<<u<<"\n";
            low[u]=min(low[u], low[v]);
        }
        else{
            low[u]=min(low[u], dfsnum[v]);
        }
    }
}
0
nimit