web-dev-qa-db-fra.com

Cycles dans un graphique non orienté

Étant donné un graphique non orienté G = ( V , E ) avec n sommets (| V | = n), comment trouvez-vous s'il contient un cycle dans O (n)?

51
Eran Kampf

Je pense que la recherche approfondie en profondeur le résout. Si un Edge inexploré mène à un nœud visité auparavant, le graphique contient un cycle. Cette condition le rend également O (n), car vous pouvez explorer n bords maximum sans le définir sur true ou sans laisser de bords inexplorés.

60
Rafał Dowgird

Un graphe G connecté et non orienté qui n'a pas de cycles est un arbre! Tout arbre a exactement n - 1 arêtes, nous pouvons donc simplement parcourir la liste des arêtes du graphique et compter les arêtes. Si nous comptons n - 1 fronts, nous retournons "oui", mais si nous atteignons le nième front, nous retournons "non". Cela prend du temps O (n) car nous regardons la plupart des n bords.

Mais si le graphe n'est pas connecté, il faudrait alors utiliser DFS. Nous pouvons traverser les arêtes et si des arêtes inexplorées conduisent au sommet visité, alors il a un cycle.

14
Ashish Pani

Vous pouvez le résoudre à l'aide de DFS. Complexité temporelle: O (n)

L'essence de l'algorithme est que si un composant/graphique connecté ne contient PAS de CYCLE, ce sera toujours un ARBRE . Voir ici pour la preuve

Supposons que le graphique n'a pas de cycle, c'est-à-dire qu'il est un arbre. Et si nous regardons un arbre, chaque Edge d'un nœud:

L'un atteint son seul et unique parent, qui est un niveau au-dessus.

2. ou atteint ses enfants, qui sont un niveau en dessous.

Donc, si un nœud a un autre Edge qui ne fait pas partie des deux décrits ci-dessus, il connectera évidemment le nœud à l'un de ses ancêtres autre que son parent. Cela formera un CYCLE.

Maintenant que les faits sont clairs, tout ce que vous avez à faire est d'exécuter un DFS pour le graphique (étant donné que votre graphique est connecté, sinon faites-le pour tous les sommets non visités), et SI vous trouvez un voisin du nœud qui est VISITÉ et PAS son parent, alors mon ami il y a un CYCLE dans le graphique, et vous avez terminé.

Vous pouvez garder la trace du parent en passant simplement le parent comme paramètre lorsque vous effectuez DFS pour ses voisins. Et comme il vous suffit d'examiner au maximum n arêtes, la complexité temporelle sera O (n).

J'espère que la réponse a aidé.

12
mb1994

Au fait, si vous savez qu'il est connecté, alors c'est simplement un arbre (donc pas de cycles) si et seulement si |E|=|V|-1. Bien sûr, ce n'est pas une petite quantité d'informations :)

7
David

La réponse est, vraiment, la première recherche étendue (ou la première recherche approfondie, cela n'a pas vraiment d'importance). Les détails résident dans l'analyse.

Maintenant, à quelle vitesse est l'algorithme?

Tout d'abord, imaginez que le graphique ne comporte aucun cycle. Le nombre d'arêtes est alors O (V), le graphe est une forêt, objectif atteint.

Maintenant, imaginez que le graphique comporte des cycles, et votre algorithme de recherche se terminera et signalera le succès dans le premier d'entre eux. Le graphique n'est pas orienté et, par conséquent, lorsque l'algorithme inspecte un Edge, il n'y a que deux possibilités: Soit il a visité l'autre extrémité du Edge, soit il l'a fait et ensuite, cet Edge ferme un cercle. Et une fois qu'il voit l'autre sommet de l'arête, ce sommet est "inspecté", il n'y a donc que O(V) de ces opérations. Le deuxième cas ne sera atteint qu'une seule fois tout au long de la course de l'algorithme.

3
jpalecek

Un DFS simple vérifie si le graphe non orienté donné a un cycle ou non.

Voici le code C++ identique.

L'idée utilisée dans le code ci-dessus est:

Si un nœud qui est déjà découvert/visité est retrouvé et n'est pas le nœud parent, alors nous avons un cycle.

Cela peut également être expliqué comme ci-dessous (mentionné par @ Rafał Dowgird

Si un Edge inexploré mène à un nœud visité auparavant, le graphique contient un cycle.

1
Chandan Mittal

Vous pouvez utiliser booster la bibliothèque de graphes et dépendances cycliques . Il a la solution pour trouver des cycles avec back_Edge une fonction.

1
Bruce

Un graphique non orienté est acyclique (c'est-à-dire une forêt) si un DFS ne produit aucun bord arrière. Puisque les arêtes arrières sont ces arêtes (u, v) reliant un sommet u à un ancêtre v dans une arborescence en profondeur d'abord, donc pas d'arête arrière signifie qu'il n'y a que des bords d'arbre, donc il n'y a pas de cycle. Nous pouvons donc simplement exécuter DFS. Si vous trouvez un Edge arrière, il y a un cycle. La complexité est O(V) au lieu de O(E + V). Comme s'il y a un Edge arrière, il doit être trouvé avant de voir |V| bords distincts. En effet, dans une forêt acyclique (non dirigée), |E| ≤ |V| + 1.

1
noob_dev

Je pense que l'hypothèse selon laquelle le graphique est connecté peut être complexe. ainsi, vous pouvez utiliser la preuve ci-dessus, que le temps d'exécution est O (| V |). sinon, | E |> | V |. rappel: le temps de fonctionnement de DFS est O (| V | + | E |).

1
gor

J'ai commencé récemment à étudier les graphiques. J'ai écrit un morceau de code en Java qui pourrait déterminer si un graphique a des cycles. J'ai utilisé DFT pour trouver des cycles dans le graphique. Au lieu de recurssion j'ai utilisé une pile pour parcourir le graphique.

À un niveau élevé DFT en utilisant une pile se fait dans les étapes suivantes

  1. Visiter un nœud
  2. Si le nœud n'est pas dans la liste visitée, ajoutez-le à la liste et poussez-le en haut de la pile
  3. Marquez le nœud en haut de la pile comme nœud actuel.
  4. Répétez ce qui précède pour chaque nœud adjacent du nœud actuel
  5. Si tous les nœuds ont été visités, faites sortir le nœud actuel de la pile

J'ai effectué un DFT à partir de chaque nœud du graphique et pendant la traversée, si j'ai rencontré un sommet que j'ai visité plus tôt, j'ai vérifié si le sommet avait une profondeur de pile supérieure à un. J'ai également vérifié si un nœud avait un bord pour lui-même et s'il y avait plusieurs bords entre les nœuds. La version de pile que j'ai écrite à l'origine n'était pas très élégante. J'ai lu le pseudo-code de la façon dont cela pourrait être fait en utilisant la récursivité et c'était bien. Voici une implémentation Java. Le tableau LinkedList représente un graphique. Avec chaque nœud et ses sommets adjacents dénotés respectivement par l'index du tableau et chaque élément

class GFG {
Boolean isCyclic(int V, LinkedList<Integer>[] alist) {
    List<Integer> visited = new ArrayList<Integer>();
    for (int i = 0; i < V; i++) {
        if (!visited.contains(i)) {
            if (isCyclic(i, alist, visited, -1))
                return true;
        }
    }
    return false;
}

Boolean isCyclic(int vertex, LinkedList<Integer>[] alist, List<Integer> visited, int parent) {
    visited.add(vertex);
    for (Iterator<Integer> iterator = alist[vertex].iterator(); iterator.hasNext();) {
        int element = iterator.next();
        if (!visited.contains(element)) {
            if (isCyclic(element, alist, visited, vertex))
                return true;
        } else if (element != parent)
            return true;
    }
    return false;
}

}

1
lalatnayak

Voici le code que j'ai écrit en C basé sur DFS pour savoir si un graphe donné est connecté/cyclique ou non. avec un échantillon de sortie à la fin. J'espère que ce sera utile :)

#include<stdio.h>
#include<stdlib.h>

/****Global Variables****/
int A[20][20],visited[20],v=0,count=0,n;
int seq[20],s=0,connected=1,acyclic=1;

/****DFS Function Declaration****/
void DFS();

/****DFSearch Function Declaration****/
void DFSearch(int cur);

/****Main Function****/
int main() 
   {    
    int i,j;

    printf("\nEnter no of Vertices: ");
    scanf("%d",&n);

    printf("\nEnter the Adjacency Matrix(1/0):\n");
    for(i=1;i<=n;i++)
            for(j=1;j<=n;j++)
        scanf("%d",&A[i][j]);

    printf("\nThe Depth First Search Traversal:\n");

    DFS();

    for(i=1;i<=n;i++)
        printf("%c,%d\t",'a'+seq[i]-1,i);

    if(connected && acyclic)    printf("\n\nIt is a Connected, Acyclic Graph!");
    if(!connected && acyclic)   printf("\n\nIt is a Not-Connected, Acyclic Graph!");
    if(connected && !acyclic)   printf("\n\nGraph is a Connected, Cyclic Graph!");
    if(!connected && !acyclic)  printf("\n\nIt is a Not-Connected, Cyclic Graph!");

    printf("\n\n");
    return 0;
   }

/****DFS Function Definition****/
void DFS()
    { 
    int i;
    for(i=1;i<=n;i++)
        if(!visited[i])
          {
        if(i>1) connected=0;
        DFSearch(i);    
              } 
    }

/****DFSearch Function Definition****/
void DFSearch(int cur) 
    {
    int i,j;
    visited[cur]=++count;

        seq[count]=cur; 
        for(i=1;i<count-1;i++)
                if(A[cur][seq[i]]) 
                   acyclic=0;

    for(i=1;i<=n;i++)
        if(A[cur][i] && !visited[i])
           DFSearch(i);

    }

/ * Exemple de sortie:

majid@majid-K53SC:~/Desktop$ gcc BFS.c

majid@majid-K53SC:~/Desktop$ ./a.out
************************************

Enter no of Vertices: 10

Enter the Adjacency Matrix(1/0):

0 0 1 1 1 0 0 0 0 0 
0 0 0 0 1 0 0 0 0 0 
0 0 0 1 0 1 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 
0 1 0 0 1 0 0 0 0 0 
0 0 0 0 0 0 0 1 0 0 
0 0 0 0 0 0 0 0 1 0 
0 0 0 0 0 0 0 0 0 1 
0 0 0 0 0 0 1 0 0 0 

The Depdth First Search Traversal:
a,1 c,2 d,3 f,4 b,5 e,6 g,7 h,8 i,9 j,10    

It is a Not-Connected, Cyclic Graph!


majid@majid-K53SC:~/Desktop$ ./a.out
************************************

Enter no of Vertices: 4

Enter the Adjacency Matrix(1/0):
0 0 1 1
0 0 1 0
1 1 0 0
0 0 0 1

The Depth First Search Traversal:
a,1 c,2 b,3 d,4 

It is a Connected, Acyclic Graph!


majid@majid-K53SC:~/Desktop$ ./a.out
************************************

Enter no of Vertices: 5

Enter the Adjacency Matrix(1/0):
0 0 0 1 0
0 0 0 1 0
0 0 0 0 1
1 1 0 0 0 
0 0 1 0 0

The Depth First Search Traversal:
a,1 d,2 b,3 c,4 e,5 

It is a Not-Connected, Acyclic Graph!

*/
1
Majid NK

Comme d'autres l'ont mentionné ... Une première recherche approfondie le résoudra. En général, la première recherche en profondeur prend O (V + E) mais dans ce cas, vous savez que le graphique a au plus O(V) bords. Vous pouvez donc simplement exécuter un DFS et une fois que vous voyez un new Edge augmenter un compteur. Lorsque le compteur a atteint V, vous n'avez pas à continuer car le graphique a certainement un cycle. Cela prend évidemment O (v).

0
HsnVahedi

Je crois que l'utilisation correcte de DFS dépend également de la façon dont vous allez représenter votre graphique dans le code. Par exemple, supposons que vous utilisez des listes adjacentes pour garder une trace des nœuds voisins et que votre graphique possède 2 sommets et un seul bord: V = {1,2} et E = {(1,2)}. Dans ce cas, à partir du sommet 1, DFS le marquera comme VISITÉ et mettra 2 dans la file d'attente. Après cela, le sommet 2 apparaîtra et comme 1 est adjacent à 2 et 1 est VISITÉ, DFS conclura qu'il y a un cycle (ce qui est faux). En d'autres termes, dans les graphiques non dirigés (1,2) et (2,1) sont les mêmes bords et vous devez coder de manière à ce que DFS ne les considère pas comme des bords différents. Garder le nœud parent pour chaque nœud visité aidera à gérer cette situation.

0
TreeStar