Graphes
Progression
#Graphes
À quoi ça sert: modéliser des connexions (réseaux, dépendances, chemins) et appliquer des algorithmes génériques. Comment: représentations (liste d’adjacence/matrice), parcours (BFS/DFS), distances (Dijkstra/Bellman‑Ford), arbres couvrants (Kruskal/Prim).
#Parcours BFS (liste d’adjacence)
#Parcours DFS (récursif et itératif)
#BFS vs DFS en un coup d’œil
- BFS parcourt le graphe par couches croissantes de distance en utilisant une file (queue). Idéal pour trouver un plus court chemin dans un graphe non pondéré.
- DFS suit un chemin en profondeur avec une pile ou la récursion. Utile pour détecter des cycles, faire un tri topologique ou explorer des composantes connexes.
#Atelier interactif : file vs pile
Parcours de graphe interactif
Quartier découpé en points de livraison. BFS visite couche par couche (itinéraire court), DFS explore un chemin avant de revenir.
Initialisation
File initialisée avec Depot. Les sommets en file sont les prochains à explorer.
File (BFS)
Visités
#Arbres couvrants minimaux (MST)
Deux approches classiques: Kruskal (tri des arêtes + union‑find) et Prim (tas sur les coupes).
#Kruskal (Union‑Find)
#Kruskal en trois étapes
- Trier les arêtes par poids croissant.
- Parcourir la liste et ajouter une arête si elle ne crée pas de cycle (testé via union-find ou composants connexes).
- Arrêter lorsque l’on a sélectionné $|V|-1$ arêtes : on obtient alors un arbre couvrant minimal.
#Prim (tas binaire)
#Exercice : Algorithme de Dijkstra pour les plus courts chemins
Implémentez l'algorithme de Dijkstra pour trouver les plus courts chemins depuis un sommet source dans un graphe pondéré.
#Instructions
- Utilisez une file de priorité (min-heap) pour stocker les sommets à visiter.
- Initialisez les distances à l'infini, sauf pour le sommet source (distance 0).
- Pour chaque sommet, mettez à jour les distances de ses voisins si un chemin plus court est trouvé.
- Marquez les sommets comme visités pour éviter les traitements multiples.
#Exemple de code
1import heapq2from collections import defaultdict3 4def dijkstra(graph, start):5 # Initialisation des distances6 distances = defaultdict(lambda: float('inf'))7 distances[start] = 08 9 # File de priorité: (distance, sommet)10 pq = [(0, start)]11 visited = set()12 13 while pq:14 current_distance, current_vertex = heapq.heappop(pq)
BFS/DFS parcourent chaque sommet/arête au plus une fois: O(|V|+|E|). Dijkstra avec tas binaire: O((|V| + |E|) log |V|).
#Tri topologique (Kahn) et détection de cycles
#Kahn en pratique
- Mettre dans la file tous les sommets de degré entrant nul.
- Retirer un sommet, l’ajouter à l’ordre topologique, décrémenter le degré entrant de ses successeurs.
- Dès qu’un successeur atteint le degré 0, l’ajouter dans la file.
- S’il reste des sommets avec un degré strictement positif, le graphe avait un cycle.
#Notations et formules utiles
- Graphe simple:
G = (V, E)
avec|V| = n
,|E| = m
. - Somme des degrés: additionner tous les degrés d’un graphe non orienté donne
2 · |E|
. - Matrice d’adjacence: une matrice binaire
A
de taillen × n
oùA[i][j] = 1
si l’arête(i, j)
existe. La puissanceA^k
permet de compter les chemins de longueurk
. - Relaxation (plus courts chemins pondérés positifs): mettre à jour
d(v)
lorsqued(u) + w(u, v)
est plus petit renforce l’invariant de Dijkstra/Bellman-Ford. - Complexités:
BFS/DFS = O(n + m)
; Dijkstra avec tas binaire coûteO((n + m) · log n)
.
- Somme des degrés donne une vérification rapide d’un graphe non orienté.
- Comptage par matrices: A^k permet de compter les chemins de longueur exacte k (utile pour motifs).
- Relaxation est l’invariant clé derrière Dijkstra/Bellman–Ford.
#Exemple: comptage de chemins avec A^k
Soit un graphe non orienté à 4 sommets avec arêtes $E={(1,2),(1,3),(2,4),(3,4)}$. $A$ vaut:
10 1 1 021 0 0 131 0 0 140 1 1 0
On vérifie que $(A^2)_4=2$ (chemins de longueur 2 de 1 vers 4: 1→2→4 et 1→3→4).
#Questions types d’examen
- Parcours : prouver que BFS retourne un plus court chemin dans un graphe non pondéré (préciser l’invariant de distance). Variante : montrer que DFS détecte un cycle en couleurs.
- MST : justifier que l’algorithme de Kruskal ne crée jamais de cycle et qu’il termine avec $|V|-1$ arêtes.
- Topologie : expliquer pourquoi la présence d’un cycle dans un graphe orienté empêche l’existence d’un ordre topologique.
- Comptage : calculer le nombre de chemins de longueur $k$ entre deux sommets via la puissance de la matrice d’adjacence.
#Checklist « graphes » avant examen
- Distinguer clairement les graphes non orientés/orientés et pondérés/non pondérés.
- Savoir reconstruire un arbre BFS/DFS à partir d’un graphe et d’un sommet racine.
- Vérifier systématiquement les hypothèses de connexité ou de pondération positive.
- Citer l’invariant associé à chaque algorithme (frontal pour BFS, pile pour DFS, composantes pour Kruskal, clé minimale pour Prim).
- Oublier de marquer les sommets visités avant de les empiler/enfiler (risque de duplication ou de boucle infinie).
- Confondre la complexité de Dijkstra O((|V| + |E|) log |V|) avec celle d’un BFS: Dijkstra n’est optimal que si tous les poids sont ≥ 0.
- Appliquer un tri topologique à un graphe non orienté ou à un graphe orienté contenant un cycle: vérifiez toujours la DAG-ité.