Graphes : représentations en Python, parcours en largeur et en profondeur, exercices corrigés de prépa
Graphes orientés et non orientés, pondérés, vocabulaire (sommets, arêtes, degré, chemin, cycle, connexité), représentations par matrice d'adjacence et par listes d'adjacence en Python, coûts en mémoire et en temps, parcours en largeur avec une file et en profondeur récursif ou avec une pile, composantes connexes, détection de cycle, plus courte distance en nombre d'arêtes, complexités.
- 17méthodes
- 34exercices corrigés
- 3problèmes
- 21pièges
- 46figures
Un graphe est formé de sommets et d'arêtes, paires de sommets, s'il est non orienté, ou d'arcs, couples de sommets, s'il est orienté ; il décrit un réseau de pistes, de routes ou de cases d'une image. Le chapitre traite la section « Introduction à la théorie des graphes » du programme d'informatique de deuxième année en MP, PSI et TSI au Maroc. Il fixe le vocabulaire (degré, chaîne et chemin, cycle et circuit, connexité, composantes connexes, graphe pondéré), puis représente un graphe en Python par une matrice d'adjacence ou par des listes d'adjacence, rangées dans une liste ou dans un dictionnaire, et compare leurs coûts : test d'une arête en mais mémoire en pour la matrice, mémoire en pour les listes. Il écrit le parcours en largeur avec une file deque, qui donne les distances en nombre d'arêtes et un plus court chemin, et le parcours en profondeur, récursif ou avec une pile, tous deux en avec des listes d'adjacence et en avec une matrice. Il les applique à l'accessibilité, à la connexité, aux composantes connexes, à la détection d'un cycle ou d'un circuit et aux zones d'une grille. Les problèmes reprennent les sujets d'informatique du CNC 2019, le réseau routier en PSI et la grille binaire en MP, et un sujet X-ENS 2026 sur la détection de cycles.
Ce que tu sauras faire
Un réseau de pistes entre des ksour, de routes entre des villes ou de cases dans une image se décrit par des points et des liens. On veut savoir d'où l'on peut aller où, par combien de liens au moins, si le réseau est d'un seul tenant, s'il contient un circuit. Un graphe est l'objet qui répond à ces questions ; deux parcours, en largeur et en profondeur, suffisent à la plupart d'entre elles.
- employer le vocabulaire des graphes orientés et non orientés ;
- représenter un graphe en Python par une matrice d'adjacence et par des listes d'adjacence, rangées dans une liste ou dans un dictionnaire, et justifier le choix par les coûts ;
- écrire les fonctions de manipulation : test d'une arête, voisins, degré, ajout, suppression ;
- écrire les parcours en largeur et en profondeur, et dérouler leur trace à la main ;
- les appliquer : sommets accessibles, distance en nombre d'arêtes, plus court chemin, connexité, composantes connexes, cycle, circuit ;
- justifier la terminaison, la correction et la complexité d'un parcours.
L'essentiel : Coûts selon la représentation Extrait gratuit
| opération | matrice | listes d'adjacence |
|---|---|---|
| tester l'arête | ||
| voisins de | ||
| degré | ||
| ajouter un sommet | ||
| mémoire | ||
| parcours complet |
Une méthode du chapitre Extrait gratuit
deque et dérouler sa trace« Écrire une fonction qui renvoie la liste des sommets dans l'ordre d'un parcours en largeur », « donner l'ordre de visite », « donner le contenu de la file à chaque étape ».
File : deque, append et popleft, en . On marque un sommet en l'enfilant. Coût avec des listes, avec une matrice (cours, section 8).
- Créer
vu, la file contenant le départ, marqué. - Tant que la file n'est pas vide : retirer à gauche, traiter, examiner les voisins dans l'ordre de la liste.
- Enfiler chaque voisin non marqué, en le marquant aussitôt.
- Pour une trace : un tableau à trois colonnes, sommet retiré, sommets enfilés, file après le tour.
F = deque()
F.append(s)
vu[s] = True
while len(F) > 0:
u = F.popleft()
for v in G[u]:
if not vu[v]:
vu[v] = True
F.append(v)
Parcours depuis la mosquée, quartier 0, sur les listes GQ.
| tour | retiré | enfilés | file après le tour |
|---|---|---|---|
| 1 | 0 | 1, 2 | [1, 2] |
| 2 | 1 | 3 | [2, 3] |
| 3 | 2 | aucun | [3] |
| 4 | 3 | 4, 5 | [4, 5] |
| 5 | 4 | aucun | [5] |
| 6 | 5 | aucun | [] |
print(largeur(GQ, 0), largeur(GQ, 4))
Le programme affiche :
[0, 1, 2, 3, 4, 5] [4, 3, 5, 1, 0, 2]
Au tour 3, les voisins 0 et 1 du quartier 2 sont déjà marqués : rien n'est enfilé.
- File par liste et
pop(0), en par retrait. pop()au lieu depopleft(): on obtient un autre parcours.- Marquer au défilement sans le dire ; oublier de marquer le départ.
Exercices 10, 12, 25 et 26 ; problèmes 1 et 2.
Un exercice corrigé Extrait gratuit
Dix puits d'une région sont reliés par des conduites. On donne la liste des conduites.
A20 = [(0, 4), (0, 9), (1, 2), (1, 7), (2, 7), (3, 8),
(5, 8)]
- Rappeler ce que renvoie
composantes(G), puis écrireliste_composantes(G), qui renvoie la liste des composantes connexes, chacune donnée par la liste de ses sommets. - Donner le nombre de composantes, la taille de la plus grande et les sommets isolés.
- Quel est le coût de
liste_composantes?
- Méthode 13 :
composantes(G)renvoie la listenum, oùnum[s]est le numéro de la composante des.
Les numéros de composantes vont de 0 à : le plus grand numéro plus un donne . On range ensuite chaque sommet dans la liste de sa composante, en un passage.
# composantes connexes, chacune liste de ses sommets
def liste_composantes(G):
num = composantes(G)
c = 0
for x in num:
c = max(c, x + 1)
C = [[] for k in range(c)]
for s in range(len(G)):
C[num[s]].append(s)
return C
G20 = listes_depuis_aretes(10, A20)
C = liste_composantes(G20)
print(composantes(G20))
print(C)
plus_grande = 0
isoles = []
for comp in C:
plus_grande = max(plus_grande, len(comp))
if len(comp) == 1:
isoles.append(comp[0])
print(len(C), plus_grande, isoles)
Le programme affiche :
[0, 1, 1, 2, 0, 2, 3, 1, 2, 0]
[[0, 4, 9], [1, 2, 7], [3, 5, 8], [6]]
4 3 [6]
Le réseau a quatre composantes : trois groupes de trois puits et le puits 6, isolé. composantes fait un parcours par composante, en en tout, puisque chaque sommet n'est enfilé qu'une fois sur l'ensemble des parcours ; le rangement ajoute .
Un parcours par sommet non encore vu, un numéro par composante : en tout. Voir la méthode 13.
Le cours détaillé, toute la fiche « L'essentiel », 17 méthodes, 21 pièges corrigés, la carte mentale, 34 exercices corrigés, 3 problèmes de concours, les fiches à mémoriser et l'assistant qui répond à tes questions. Les trois premiers chapitres de chaque matière sont offerts avec un compte d’essai gratuit.
Ouvrir l'essai gratuitVoir les offresPlan du cours
- 1. Des réseaux partout
- 1.1 Les pistes après l'orage
- 1.2 Ce que l'on garde du croquis
- 1.3 D'autres réseaux
- 2. Graphes non orientés
- 2.1 Définition
- 2.2 Voisins, ordre, degré
- 2.3 Somme des degrés
- 2.4 Chaînes et cycles
- 2.5 Connexité
- 2.6 Arbres, forêts et nombre d'arêtes
- 3. Graphes orientés
- 3.1 Arcs, successeurs, prédécesseurs
- 3.2 Degrés sortant et entrant
- 3.3 Chemins et circuits
- 3.4 Les mots des sujets
- 4. Graphes pondérés
- 4.1 Étiquettes et poids
- 4.2 Ce que l'on en fera
- 5. Représenter un graphe en Python
- 5.1 Le Python employé dans ce chapitre
- 5.2 Numéroter les sommets
- 5.3 La matrice d'adjacence
- 5.4 Les listes d'adjacence dans une liste
- 5.5 Les listes d'adjacence dans un dictionnaire
- 5.6 Graphe pondéré
- 5.7 Passer d'une représentation à l'autre
- 5.8 Compter les chemins par les puissances de la matrice
- 6. Manipuler un graphe et choisir sa représentation
- 6.1 Les fonctions élémentaires
- 6.2 Le coût en temps
- 6.3 Le coût en mémoire
- 6.4 Choisir
- 6.5 Programmer avec des fonctions d'interface
- 7. Piles et files
- 7.1 Rappel
- 7.2 Le défaut de la file par liste
- 7.3 Le type
deque - 7.4 Une mesure
- 8. Le parcours en largeur
- 8.1 L'idée
- 8.2 Marquer, et quand
- 8.3 Le code
- 8.4 La trace
- 8.5 Terminaison et correction
- 8.6 Le coût
- 8.7 L'ordre de visite n'est pas unique
- 9. Distances en nombre d'arêtes et plus courts chemins
- 9.1 Les distances
- 9.2 Les prédécesseurs et l'arbre du parcours
- 9.3 Reconstruire un chemin
- 9.4 Pourquoi c'est le plus court
- 9.5 Ce que le parcours en largeur ne fait pas
- 10. Le parcours en profondeur
- 10.1 L'idée
- 10.2 La version récursive
- 10.3 Trace et arbre des appels
- 10.4 La version avec une pile
- 10.5 Deux ordres, deux parcours en profondeur
- 10.6 La pile d'appels
- 10.7 Terminaison, correction et coût
- 11. Applications des parcours
- 11.1 Accessibilité
- 11.2 Connexité
- 11.3 Composantes connexes
- 11.4 Un cycle dans un graphe non orienté
- 11.5 Un circuit dans un graphe orienté
- 11.6 Une grille vue comme un graphe
- 12. Bilan
- 12.1 Les complexités
- 12.2 Quel parcours pour quelle question
- 12.3 Vers le chapitre 12
Les méthodes du chapitre
Chaque méthode est une fiche : quand l'utiliser, le rappel de cours, les étapes, la rédaction attendue sur la copie, un exemple traité, les erreurs à éviter.
- Modéliser une situation par un graphe
- Employer le vocabulaire : degrés, chaînes, cycles, connexité
- Écrire et lire la matrice d'adjacence
- Écrire les listes d'adjacence, dans une liste ou un dictionnaire
- Passer d'une représentation à l'autre
- Représenter un graphe pondéré
- Écrire les fonctions élémentaires : test, voisins, degré, ajout, suppression
- Choisir une représentation et justifier par les coûts
- Écrire un parcours en largeur avec une
dequeet dérouler sa trace - Calculer les distances en nombre d'arêtes et reconstruire un plus court chemin
- Écrire un parcours en profondeur, récursif ou avec une pile
- Justifier terminaison, correction et complexité d'un parcours
- Tester la connexité, compter et lister les composantes connexes
- Détecter un cycle ou un circuit
- Traiter une grille ou un ensemble d'états comme un graphe sans le construire
- Lire un sujet de graphes : représentation, conventions, fonctions fournies
- Compter les chemins de longueur par les puissances de la matrice d'adjacence
Les exercices corrigés
Chaque exercice a ses indications, sa correction rédigée et ce qu'il faut en retenir.
Application directe (13)
- Le vocabulaire sur un dessin
- La somme des degrés
- Pistes à sens unique
- Le graphe de huit pays
- Lire une matrice d'adjacence
- Deux pays voisins
- Voisins et arêtes depuis la matrice
- Des noms aux numéros
- Un graphe pondéré en trois écritures
- Un parcours en largeur à la main
- Un parcours en profondeur à la main
- File ou pile
- Trois questions de cours
Entraînement (15)
- Matrice ou liste d'adjacence
- Ajouter et supprimer une arête
- Le graphe miroir
- Les distances depuis la coopérative
- Le chemin par le moins de pistes
- Connexe ou non
- Les composantes d'un réseau
- Un cycle dans un réseau
- La liste qui se souvient
- Marquer dans une liste
- Le parcours en profondeur sans récursivité
pop(0)contrepopleft- Les sommets à distance k
- Sortir d'un labyrinthe
- Le parcours en largeur sur une matrice
Approfondissement (6)
- Des tâches dans le bon ordre
- Le sommet le plus central
- Les pistes qu'il ne faut pas perdre
- Assez d'arêtes pour être connexe
- Deux bidons au puits
- Rendre la monnaie par un parcours en largeur
Les problèmes de concours
Des extraits de sujets réels, corrigés en entier, avec la durée conseillée et les méthodes mobilisées.
- Chemins dans un réseau routier
- Les zones d'une grille binaire
- Détecter un cycle avant d'ajouter un arc
Questions fréquentes
Matrice d'adjacence ou listes d'adjacence : laquelle choisir ?
Pour sommets numérotés de 0 à , la matrice s'écrit M = [[0] * n for i in range(n)], puis M[u][v] = 1 pour chaque arête ; les listes s'écrivent G = [[] for i in range(n)], puis G[u].append(v), et une arête d'un graphe non orienté y est rangée deux fois. La matrice teste une arête en , mais occupe cases quel que soit le nombre d'arêtes et demande pour trouver les voisins d'un sommet. Les listes occupent , pour arêtes, donnent les voisins aussitôt, mais testent une arête en . Pour 1 000 sommets de quatre voisins chacun, la matrice a un million de cases, les listes 4 000 éléments. La matrice convient aux petits graphes et aux graphes denses ; les listes d'adjacence conviennent aux graphes peu denses, comme les réseaux réels, et aux parcours.
Comment écrire un parcours en largeur en Python ?
Avec une file : from collections import deque, F = deque(), F.append(s) et vu[s] = True pour le départ. Tant que la file n'est pas vide, on retire u = F.popleft(), on le traite, puis on examine ses voisins dans l'ordre de la liste : chaque voisin non marqué est marqué aussitôt et enfilé. On marque un sommet au moment où on l'enfile, pour qu'il n'entre qu'une fois dans la file. On n'écrit jamais F.pop(0) sur une liste : ce retrait décale tous les éléments et coûte pour une file de éléments ; mesuré sur 160 000 éléments, vider la file ainsi est environ 300 fois plus lent qu'avec popleft, en temps constant.
Comment trouver un plus court chemin avec un parcours en largeur ?
On initialise dist à partout sauf dist[s] = 0, et pred à . Pendant le parcours en largeur, quand on découvre depuis , on pose dist[v] = dist[u] + 1 et pred[v] = u. Les sommets sortent de la file par distances croissantes : à la fin, dist[v] est la distance de à en nombre d'arêtes. Pour obtenir un plus court chemin jusqu'à , on remonte pred depuis jusqu'à , puis on retourne la liste ; si dist[t] vaut , n'est pas accessible. Le coût est celui du parcours, . Ce chemin est le plus court en nombre d'arêtes, pas en kilomètres : sur les pistes du cours, il rend un itinéraire de 45 km alors qu'un autre, avec le même nombre de pistes, en fait 37. Les longueurs relèvent d'autres algorithmes, au chapitre suivant.
Parcours en profondeur : version récursive ou avec une pile ?
La version récursive visite un sommet, le marque, puis s'appelle sur chacun de ses voisins non vus. Elle empile un appel par sommet de la chaîne en cours, et Python limite par défaut la pile à 1 000 appels en attente : sur une chaîne de 2 000 sommets parcourue depuis une extrémité, elle s'arrête sur une erreur RecursionError. La version avec une pile explicite dépile un sommet, le marque et le visite s'il n'est pas encore vu, puis empile ses voisins non vus ; un sommet peut alors être empilé plusieurs fois, et la pile contenir jusqu'à éléments. Les deux sont des parcours en profondeur, mais pas dans le même ordre : la pile prend d'abord le dernier voisin de la liste. Empiler les voisins en ordre inverse redonne l'ordre de la version récursive. Pour un très grand graphe, on préfère la version avec une pile.
Quelle est la complexité d'un parcours en largeur ou en profondeur ?
Avec des listes d'adjacence, : chaque sommet est enfilé ou visité au plus une fois, puisqu'on le marque, et chaque liste d'adjacence est parcourue une fois ; la somme de leurs longueurs vaut dans un graphe non orienté, puisque la somme des degrés vaut . Avec une matrice, trouver les voisins d'un sommet lit une ligne entière, en : le parcours coûte , même si le graphe a peu d'arêtes. Le meilleur cas, un départ sans voisin, est en , pour la création de la liste vu ; le pire cas, tous les sommets accessibles, en avec des listes et en avec une matrice. Pour 4 000 sommets et environ 8 000 arêtes, le parcours en largeur prend environ 5 ms avec des listes et 2 s avec une matrice.
Comment tester la connexité d'un graphe ou y détecter un cycle ?
Un graphe non orienté est connexe si un parcours depuis un sommet quelconque atteint les sommets. Pour les composantes connexes, on lance un parcours depuis chaque sommet non encore atteint et l'on donne le même numéro à tous les sommets qu'il atteint : en tout. Pour un cycle dans un graphe non orienté, on retient le père de chaque sommet découvert : un voisin déjà marqué qui n'est pas le père révèle un cycle ; l'arête qui ramène au père, elle, n'en fait pas. Dans un graphe orienté, rencontrer un sommet déjà vu ne prouve rien, car deux chemins peuvent arriver au même sommet : on fait un parcours en profondeur à trois états, non vu, en cours, terminé, et un arc vers un sommet en cours ferme un circuit. Ces tests coûtent avec des listes d'adjacence.
Comment y accéder
Ce chapitre fait partie de l'accès complet. Les trois premiers chapitres de chaque matière (deux par bloc en économie et droit, en management et gestion) sont offerts avec un compte d'essai gratuit. Créer un compte d'essai ou se connecter. Pour l'accès complet et les tarifs, vois les offres.