Parcourir les graphes
Comment explorer un réseau sans tourner en rond ?
Environ 45 minutes
- Exécuter et expliquer les deux parcours.
- Reconstruire un chemin à partir des prédécesseurs.
- Détecter des cycles avec le critère adapté.
Comment explorer un réseau sans tourner en rond ?
Un labyrinthe peut être décrit par ses intersections et ses passages. Trouver une sortie demande de choisir où avancer, de mémoriser les lieux déjà atteints et parfois de revenir en arrière. Un parcours de graphe rend cette exploration systématique. Nous travaillerons avec des graphes finis, puis distinguerons la recherche d’un chemin de celle d’un chemin comportant le moins d’arêtes. Tu dois déjà savoir manipuler des listes, des dictionnaires et des fonctions, et reconnaître une file et une pile.
Représenter ce qui est relié
Un sommet représente un objet et une arête une relation non orientée. Dans un graphe orienté, un arc indique un sens autorisé : pouvoir aller de A vers B ne garantit pas le retour. Une liste d’adjacence associe à chaque sommet ses voisins, ou ses successeurs dans le cas orienté. Tous les sommets, y compris les sommets isolés, doivent apparaître comme clés du dictionnaire. On suppose ici des graphes simples, sans boucle ni arêtes multiples.
Pour le graphe dessiné, les listes, dans cet ordre, sont A : B, C ; B : A, D ; C : A, D, E ; D : B, C ; E : C. Chaque arête apparaît dans deux listes parce que le graphe est non orienté. L’ordre des voisins détermine les choix quand plusieurs déplacements sont possibles. Il faut donc le préciser pour donner un ordre de parcours unique ; le dessin seul ne suffit pas à l’imposer.
Un chemin est une succession de sommets reliés, respectant les orientations éventuelles. Sa longueur compte les arêtes, et non les sommets. Un chemin de A vers A peut être vide et avoir longueur zéro. Un cycle simple revient à son point de départ sans répéter les autres sommets. Parcourir depuis une origine atteint seulement les sommets accessibles depuis elle ; un graphe non connexe nécessite plusieurs départs pour être exploré entièrement.
Explorer en largeur
Le parcours en largeur utilise une file : le premier sommet entré est le premier retiré. On découvre d’abord l’origine, puis tous ses voisins, puis les sommets encore inconnus atteints à partir de ces voisins. Marquer un sommet dès son entrée dans la file empêche de l’ajouter plusieurs fois. Être découvert signifie être connu du parcours ; être traité signifie avoir examiné ses voisins. Ces deux moments sont différents.
Construire le parcours en largeur
- Créer une file contenant seulement le départ, un ensemble de sommets découverts contenant ce départ et un dictionnaire de distances avec la valeur zéro au départ.
- Tant que la file n’est pas vide, retirer sa tête. Pour chaque voisin encore inconnu, le marquer immédiatement, enregistrer sa distance comme celle du sommet courant plus un et mémoriser le sommet courant comme prédécesseur.
- Ajouter chaque nouveau voisin à la fin de la file. Terminer lorsque la file est vide, puis utiliser les distances ou les prédécesseurs selon la question.
Exemple résolu
Depuis A, exécute le parcours en largeur du graphe dessiné, avec les listes de voisins données.
- Étape 1 : Initialement, la file contient A. Traiter A découvre B et C : la file devient B, C et leurs distances valent 1.
- Étape 2 : Traiter B découvre D, mais A est déjà marqué. La file devient C, D ; la distance de D vaut 2 et son prédécesseur est B.
- Étape 3 : Traiter C découvre E. D est déjà découvert : on ne le réinsère pas. La file devient D, E ; la distance de E vaut 2.
- Étape 4 : Traiter D, puis E, ne découvre rien de nouveau. La file est vide.
Résultat : L’ordre de traitement est A, B, C, D, E. Un plus court chemin de A vers E est A–C–E, de longueur 2.
Piège à éviter : Compter les trois sommets de A–C–E comme une distance de 3 confond sommets et arêtes.
Pourquoi la première distance trouvée est-elle minimale ? Tous les sommets de distance zéro sont traités avant ceux de distance un, puis ceux de distance un avant ceux de distance deux. Si un sommet était accessible par un chemin plus court, il aurait été découvert lors d’un niveau antérieur. Ce raisonnement concerne le nombre d’arêtes. Quand des arêtes portent des durées différentes, la largeur ne garantit pas la durée minimale.
À toi d'essayer
On ajoute un sommet F relié seulement à E. Quelle distance le parcours depuis A attribue-t-il à F ? Justifie sans recommencer toute la trace.
Voir la réponseMasquer la réponse
La distance de E vaut 2. Le seul accès à F passe par E, puis une arête supplémentaire : la distance de F vaut 3.
Explorer en profondeur et reconstruire un chemin
Le parcours en profondeur suit une branche jusqu’à ne plus pouvoir avancer, puis revient au dernier choix encore disponible. Une fonction récursive réalise ce comportement grâce à la pile des appels. À l’entrée dans un sommet, elle le marque ; elle appelle ensuite la même fonction pour chaque voisin non marqué, dans l’ordre fixé. Un appel se termine quand tous les voisins ont été examinés.
Exemple résolu
Donne l’ordre de découverte du parcours récursif en profondeur depuis A, avec les mêmes listes de voisins.
- Étape 1 : Marquer A, puis appeler le parcours sur B, son premier voisin inconnu.
- Étape 2 : Depuis B, ignorer A déjà marqué et entrer dans D. Depuis D, ignorer B et entrer dans C.
- Étape 3 : Depuis C, A et D sont déjà marqués ; entrer dans E. E n’a aucun voisin inconnu.
- Étape 4 : Les appels de E, C, D, B et A se terminent successivement. Le second voisin C de A est déjà marqué.
Résultat : L’ordre de découverte est A, B, D, C, E. Le chemin de découverte vers E est A–B–D–C–E, plus long que le chemin trouvé en largeur.
Piège à éviter : Le premier chemin trouvé en profondeur n’est pas nécessairement le plus court.
Pour reconstruire un chemin, mémorise pour chaque nouveau sommet le prédécesseur qui l’a découvert. Pars de la destination, remonte jusqu’au départ et renverse la liste obtenue. Si la destination n’a pas été découverte, annonce qu’aucun chemin n’existe depuis cette origine. Ne fabrique pas un chemin en reliant arbitrairement les sommets successifs de l’ordre de parcours : deux sommets traités consécutivement ne sont pas forcément voisins.
Avec des listes d’adjacence, chaque sommet est découvert une fois et chaque liste est parcourue une fois. Le coût est donc O(n + m), pour n sommets et m arêtes, avec un ensemble permettant une recherche efficace. Dans un graphe non orienté, chaque arête est examinée deux fois, ce qui ne change pas cet ordre de grandeur. Une matrice impose au contraire de regarder une ligne de n cases par sommet.
À toi d'essayer
Dans un graphe orienté, les seuls arcs sont P vers Q et R vers Q. Un parcours depuis P atteint-il R ?
Correction détaillée pas à pasDans ce chapitre : 2 corrections détaillées.
Détecter un cycle sans confondre retour et répétition
Dans un graphe simple non orienté, chaque arête apparaît aussi dans le sens du retour. Pendant un parcours récursif en profondeur, retrouver le parent du sommet courant n’indique donc pas un cycle. En revanche, rencontrer un autre voisin déjà découvert révèle un cycle. Il faut retenir le parent et distinguer ces deux cas. Pour chercher un cycle dans tout le graphe, répète cette exploration depuis chaque sommet encore inconnu.
Exemple résolu
Détecte un cycle dans le triangle de sommets A, B et C, avec les voisins examinés par ordre alphabétique.
- Étape 1 : Depuis A, découvrir B en notant que son parent est A.
- Étape 2 : Depuis B, ignorer le retour vers A, puis découvrir C : C n’était pas encore visité.
- Étape 3 : Depuis C, examiner A. A est déjà découvert et n’est pas le parent B de C.
Résultat : Un cycle est détecté : A–B–C–A. La détection intervient depuis C, et non au moment où B découvre C.
Piège à éviter : Un voisin non découvert ne prouve pas un cycle ; il prolonge simplement la branche en cours.
Dans un graphe orienté, un sommet déjà terminé peut être rejoint sans créer de cycle. On utilise trois états : non découvert, en cours d’exploration, terminé. Un arc vers un sommet en cours d’exploration referme une branche active et révèle un cycle orienté. Un arc vers un sommet terminé ne suffit pas. Cette distinction est essentielle dans un graphe de dépendances, où plusieurs tâches peuvent dépendre d’une même tâche déjà traitée.
À toi d'essayer
Un parcours orienté suit A vers B puis B vers D, termine D puis B, et explore ensuite A vers C puis C vers D. Quel raisonnement permet de décider si cette dernière rencontre prouve un cycle ?
Correction détaillée pas à pasDans ce chapitre : 2 corrections détaillées.
Au bac et mise au point
Au bac : justifier une trace et un programme
- Préciser les données, le sens des arcs, le départ, l’ordre des voisins et le résultat attendu. Nommer explicitement la file ou la pile utilisée.
- Écrire une table d’exécution distinguant le sommet courant, les sommets découverts et les éléments en attente. Vérifier chaque insertion et chaque retrait.
- Relier les prédécesseurs à de vraies arêtes. Pour une question de cycle, nommer exactement le sommet déjà rencontré et son état.
- Tester ensuite le programme sur un sommet isolé, une chaîne, un cycle et deux composantes séparées. Comparer une propriété attendue et pas seulement un affichage.
En Python, une file peut être implémentée avec collections.deque, append pour enfiler et popleft pour défiler. Une liste avec pop(0) déplace ses éléments et dégrade le coût. Pour une exploration récursive, créer un nouvel ensemble de visités à chaque appel détruit la mémoire commune ; il faut partager cet ensemble entre les appels du même parcours. Une très longue chaîne peut aussi dépasser la limite pratique de récursion : une pile explicite permet de maîtriser cet aspect.
Erreurs fréquentes : changer l’ordre des voisins en cours de trace ; marquer trop tard sans contrôler les doublons ; appliquer la règle du parent à un graphe orienté ; annoncer un chemin entre deux composantes disjointes. À chaque fois, reviens aux données et à la propriété précise que ton parcours garantit.
Vocabulaire
- File :
- Structure où le premier élément ajouté est retiré en premier.
- Pile :
- Structure où le dernier élément ajouté est retiré en premier.
- Prédécesseur de découverte :
- Sommet depuis lequel un autre sommet a été découvert pendant le parcours.
- Connexité :
- Dans un graphe non orienté, existence d’un chemin entre toute paire de sommets.
Bilan
| Repère | Sens |
|---|---|
| Largeur | File ; distances minimales en nombre d’arêtes. |
| Profondeur | Pile ou récursion ; exploration d’une branche puis retour. |
| Cycle | Critère différent selon que le graphe est orienté ou non. |
S’entraîner
Pour poursuivre ton entraînement, ouvre la rubrique S’entraîner du site, à l’adresse /exercices. Reprends d’abord les questions de cette leçon sans afficher les réponses, puis compare ton raisonnement à la correction.
À retenir
- La largeur traite les sommets par distance croissante en nombre d’arêtes.
- La profondeur utilise une pile explicite ou celle des appels récursifs.
- Un parcours depuis un départ ne découvre que les sommets accessibles.
- Dans un graphe non orienté simple, le retour vers le parent ne constitue pas un cycle.
Fiche de révision
Pour choisir
rechercher un minimum de passages conduit à la largeur ; explorer complètement une branche conduit à la profondeur.
Pour reconstruire
remonter les parents depuis l’arrivée, puis renverser l’ordre. Une arrivée non découverte signifie une absence de chemin.
Pour vérifier
dans A–B–C, il y a deux arêtes. Pour les cycles orientés, distinguer encore actif et déjà terminé.
Et maintenant ?
Tu as lu le cours. L’étape suivante tient en une action.
Relis le coursReprends l’essentiel avant de passer à la suite.Prolonge l’expérience !
Continue à explorer ce chapitre à travers de nouvelles activités, des défis et des expériences interactives. Avec Premium, l’apprentissage continue !
Ici, tu débloques aussi 2 corrections détaillées.
Parent ? Suivez les progrès de votre enfant.
