Partie 2 : Algorithmes sur les graphes¶
Programme officiel (B.O.)¶
B.O. spécial n° 8 du 25 juillet 2019 - NSI Terminale
| Contenus | Capacités attendues | Commentaires |
|---|---|---|
| Algorithmes sur les graphes. | Parcourir un graphe en profondeur d'abord, en largeur d'abord. Repérer la présence d'un cycle dans un graphe. Chercher un chemin dans un graphe. | Le parcours d'un labyrinthe et le routage dans Internet sont des exemples d'algorithme sur les graphes. L'exemple des graphes permet d'illustrer l'utilisation des classes en programmation. |
1. Rappels sur les graphes¶
1.1 Représentations¶
Un graphe peut être représenté par :
- une matrice d'adjacence : tableau 2D où
matrice[i][j] = 1si les sommets i et j sont reliés ; - un dictionnaire de listes d'adjacence : pour chaque sommet, la liste de ses voisins.
# Graphe non orienté
# A --- B
# | / |
# | / |
# C --- D
# Dictionnaire d'adjacence
graphe = {
'A': ['B', 'C'],
'B': ['A', 'C', 'D'],
'C': ['A', 'B', 'D'],
'D': ['B', 'C']
}
# Matrice d'adjacence (A=0, B=1, C=2, D=3)
matrice = [
[0, 1, 1, 0],
[1, 0, 1, 1],
[1, 1, 0, 1],
[0, 1, 1, 0]
]
2. Parcours en profondeur (DFS)¶
2.1 Principe¶
Le parcours en profondeur (Depth-First Search, DFS) explore un graphe en allant le plus loin possible avant de revenir en arrière. Il utilise une pile (ou la récursion).
2.2 Algorithme récursif¶
def dfs_recursif(graphe, sommet, visites=None):
if visites is None:
visites = set()
visites.add(sommet)
print(sommet, end=' ')
for voisin in graphe[sommet]:
if voisin not in visites:
dfs_recursif(graphe, voisin, visites)
return visites
2.3 Algorithme itératif (avec pile)¶
def dfs_iteratif(graphe, depart):
visites = set()
pile = [depart]
resultat = []
while pile:
sommet = pile.pop()
if sommet not in visites:
visites.add(sommet)
resultat.append(sommet)
for voisin in reversed(graphe[sommet]):
if voisin not in visites:
pile.append(voisin)
return resultat
2.4 Application : le labyrinthe¶
Un labyrinthe peut être modélisé comme un graphe où chaque case est un sommet et les passages sont des arêtes. Le DFS permet de trouver un chemin de l'entrée à la sortie.
3. Parcours en largeur (BFS)¶
3.1 Principe¶
Le parcours en largeur (Breadth-First Search, BFS) explore un graphe niveau par niveau : d'abord les voisins directs, puis les voisins des voisins, etc. Il utilise une file (FIFO).
3.2 Algorithme¶
from collections import deque
def bfs(graphe, depart):
visites = set([depart])
file = deque([depart])
resultat = []
while file:
sommet = file.popleft()
resultat.append(sommet)
for voisin in graphe[sommet]:
if voisin not in visites:
visites.add(voisin)
file.append(voisin)
return resultat
3.3 Comparaison DFS vs BFS¶
| Critère | DFS (profondeur) | BFS (largeur) |
|---|---|---|
| Structure | Pile (ou récursion) | File |
| Exploration | En profondeur d'abord | Niveau par niveau |
| Chemin trouvé | Un chemin (pas forcément le plus court) | Le plus court chemin (en nombre d'arêtes) |
| Mémoire | Proportionnelle à la hauteur | Proportionnelle à la largeur |
4. Chercher un chemin¶
4.1 Chemin entre deux sommets (DFS)¶
def chemin_dfs(graphe, depart, arrivee, visites=None):
if visites is None:
visites = set()
visites.add(depart)
if depart == arrivee:
return [depart]
for voisin in graphe[depart]:
if voisin not in visites:
chemin = chemin_dfs(graphe, voisin, arrivee, visites)
if chemin is not None:
return [depart] + chemin
return None
4.2 Plus court chemin (BFS)¶
Le BFS garantit de trouver le plus court chemin (en nombre d'arêtes).
from collections import deque
def plus_court_chemin(graphe, depart, arrivee):
if depart == arrivee:
return [depart]
visites = {depart}
file = deque([(depart, [depart])])
while file:
sommet, chemin = file.popleft()
for voisin in graphe[sommet]:
if voisin not in visites:
nouveau_chemin = chemin + [voisin]
if voisin == arrivee:
return nouveau_chemin
visites.add(voisin)
file.append((voisin, nouveau_chemin))
return None
5. Détecter un cycle¶
5.1 Cycle dans un graphe non orienté¶
Un cycle est un chemin qui revient à son point de départ. Pour le détecter dans un graphe non orienté, on effectue un DFS : si on rencontre un sommet déjà visité qui n'est pas le parent direct, il y a un cycle.
def contient_cycle(graphe):
visites = set()
def dfs(sommet, parent):
visites.add(sommet)
for voisin in graphe[sommet]:
if voisin not in visites:
if dfs(voisin, sommet):
return True
elif voisin != parent:
return True
return False
for sommet in graphe:
if sommet not in visites:
if dfs(sommet, None):
return True
return False
5.2 Graphe sans cycle (arbre)¶
Un graphe connexe sans cycle est un arbre. Il a exactement \(n - 1\) arêtes pour \(n\) sommets.
6. Applications¶
| Application | Graphe | Algorithme |
|---|---|---|
| Labyrinthe | Cases = sommets, passages = arêtes | DFS ou BFS |
| GPS / Itinéraire | Carrefours = sommets, routes = arêtes pondérées | Dijkstra (BFS pondéré) |
| Réseau social | Personnes = sommets, relations = arêtes | BFS (degré de séparation) |
| Routage Internet | Routeurs = sommets, liens = arêtes | OSPF (Dijkstra), RIP (Bellman-Ford) |
| Ordonnancement | Tâches = sommets, dépendances = arcs | Tri topologique (DFS) |
À retenir¶
| Concept | Description |
|---|---|
| DFS | Parcours en profondeur, utilise une pile ou la récursion |
| BFS | Parcours en largeur, utilise une file, trouve le plus court chemin |
| Cycle | Chemin revenant au point de départ (DFS pour détecter) |
| Chemin | Suite de sommets reliés par des arêtes |
| Plus court chemin | Chemin avec le minimum d'arêtes (BFS) ou de coût (Dijkstra) |