Retour à la page d'accueilNSI · Terminale (maths expertes) · Changer de matière
Commencer gratuitement
Terminale (maths expertes) · NSI
Structures de données, bases de données et algorithmique · Structures de données

Graphes et parcours

Tu ouvres l'application du métro lyonnais : elle te propose en une seconde le trajet de Perrache à Part-Dieu. Comment un programme « voit-il » un réseau de stations et de lignes ? Il le modélise par un graphe, puis le parcourt avec un algorithme. C'est ce que tu vas apprendre ici.

Vocabulaire et représentations

Un graphe est formé de sommets (les stations, les routeurs, les modules…) reliés par des arêtes (graphe non orienté : la liaison marche dans les deux sens) ou par des arcs (graphe orienté : la liaison a un sens).

Un graphe non orienté à quatre sommets A, B, C, D et quatre arêtes ABCD Arêtes : A–B, A–C, B–C, C–D Degré de C : 3 Degré de D : 1

Il y a trois façons usuelles de représenter un graphe en Python.

  • Matrice d'adjacence : un tableau M de $n \times n$ cases, où M[i][j] = 1 s'il existe une arête (ou un arc $i \to j$), et 0 sinon.

  • Listes de successeurs (ou de voisins) : pour chaque sommet, la liste des sommets atteignables en une étape.

  • Listes de prédécesseurs : pour chaque sommet, la liste des sommets qui pointent vers lui (graphes orientés).

Pour le graphe de la figure (ordre A, B, C, D) :

$$M = \begin{pmatrix} 0 & 1 & 1 & 0 \\ 1 & 0 & 1 & 0 \\ 1 & 1 & 0 & 1 \\ 0 & 0 & 1 & 0 \end{pmatrix}$$
g = {"A": ["B", "C"], "B": ["A", "C"], "C": ["A", "B", "D"], "D": ["C"]}

Pourquoi ces deux choix ? La matrice permet de tester « $i$ et $j$ sont-ils reliés ? » en un seul accès, mais occupe $n^2$ cases. Les listes n'enregistrent que les liaisons existantes : c'est plus économe quand il y a peu d'arêtes.

Entraîne-toi★☆☆Pas commencé
Un graphe non orienté a pour sommets A, B, C, D et pour arêtes : A–B, A–C, B–C, C–D. a) Donner la matrice d'adjacence (sommets dans l'ordre A, B, C, D). b) Donner la…

Un graphe non orienté a pour sommets A, B, C, D et pour arêtes : A–B, A–C, B–C, C–D.

  1. a) Donner la matrice d'adjacence (sommets dans l'ordre A, B, C, D).
  1. b) Donner la liste d'adjacence de chaque sommet.
  1. c) Donner le degré de chaque sommet.
Résoudre
Entraîne-toi★☆☆Pas commencé
Un graphe orienté a pour sommets 0, 1, 2, 3 et pour arcs : 0→1, 0→2, 1→3, 2→3, 3→0. a) Donner la liste des successeurs de chaque sommet. b) Donner la liste des…

Un graphe orienté a pour sommets 0, 1, 2, 3 et pour arcs : 0→1, 0→2, 1→3, 2→3, 3→0.

  1. a) Donner la liste des successeurs de chaque sommet.
  1. b) Donner la liste des prédécesseurs de chaque sommet.
  1. c) Écrire la matrice d'adjacence et expliquer pourquoi elle n'est pas symétrique.
Résoudre
Entraîne-toi★☆☆Pas commencé
Soit le graphe orienté défini par le dictionnaire Python g = {0: [1, 2], 1: [2], 2: [0, 3], 3: []}. a) Dessiner (décrire) ce graphe. b) Indiquer si les chemins 0→1→2→3…

Soit le graphe orienté défini par le dictionnaire Python g = {0: [1, 2], 1: [2], 2: [0, 3], 3: []}.

  1. a) Dessiner (décrire) ce graphe.
  1. b) Indiquer si les chemins 0→1→2→3 et 3→2→0 existent.
  1. c) Citer un cycle de ce graphe.
Résoudre
Entraîne-toi★★☆Pas commencé
Un réseau de métro simplifié à Lyon relie les stations Bellecour (Be), Perrache (Pe), Cordeliers (Co), Hôtel de Ville (Ho) et Part-Dieu (Pa) : Be–Pe, Be–Co, Co–Ho,…

Un réseau de métro simplifié à Lyon relie les stations Bellecour (Be), Perrache (Pe), Cordeliers (Co), Hôtel de Ville (Ho) et Part-Dieu (Pa) : Be–Pe, Be–Co, Co–Ho, Be–Ho, Ho–Pa.

  1. a) Représenter ce réseau par une matrice d'adjacence.
  1. b) Donner le nombre minimal de liaisons (arêtes) à parcourir pour aller de Pe à Pa et un chemin correspondant.
  1. c) Écrire une fonction Python degre(matrice, i) qui renvoie le degré du sommet i.
Résoudre

Parcours en largeur et en profondeur

Parcourir un graphe, c'est visiter ses sommets un par un, depuis un sommet de départ, sans jamais en visiter un deux fois. Deux stratégies :

  • En largeur (BFS) : on visite d'abord tous les voisins du départ, puis les voisins des voisins, etc. On utilise une file (FIFO).

  • En profondeur (DFS) : on s'enfonce le plus loin possible, puis on revient en arrière. On utilise une pile (LIFO) ou la récursivité.

Le parcours en largeur explore « par cercles » autour du départ : le premier chemin trouvé vers un sommet est donc de longueur minimale (en nombre d'arêtes).

from collections import deque

def parcours_largeur(g, s):

    visites = [s]

    file = deque([s])

    while file:

        u = file.popleft()

        for v in g[u]:

            if v not in visites:

                visites.append(v)

                file.append(v)

    return visites

def parcours_profondeur(g, s, visites=None):

    if visites is None:

        visites = []

    visites.append(s)

    for v in g[s]:

        if v not in visites:

            parcours_profondeur(g, v, visites)

    return visites

Pour obtenir la distance de s à chaque sommet, on remplace la liste visites par un dictionnaire dist : dist[s] = 0, et lorsqu'on découvre v depuis u, on pose dist[v] = dist[u] + 1.

Entraîne-toi★★☆Pas commencé
Soit le graphe non orienté défini par g = {"A": ["B", "C"], "B": ["A", "D"], "C": ["A", "D", "E"], "D": ["B", "C"], "E": ["C"]}. a) Donner le parcours en largeur à…

Soit le graphe non orienté défini par g = {"A": ["B", "C"], "B": ["A", "D"], "C": ["A", "D", "E"], "D": ["B", "C"], "E": ["C"]}.

  1. a) Donner le parcours en largeur à partir de A (en visitant les voisins dans l'ordre alphabétique).
  1. b) Donner le parcours en profondeur à partir de A (même convention).
  1. c) Donner un chemin de A à E de longueur minimale.
Résoudre
Entraîne-toi★★★Pas commencé
Écrire en Python une fonction parcours_largeur(g, s) qui prend un graphe g sous forme de dictionnaire de listes d'adjacence et un sommet s, et renvoie la liste des…

Écrire en Python une fonction parcours_largeur(g, s) qui prend un graphe g sous forme de dictionnaire de listes d'adjacence et un sommet s, et renvoie la liste des sommets visités dans l'ordre d'un parcours en largeur.

  1. a) Préciser la structure de données utilisée et justifier ce choix.
  1. b) Écrire la fonction.
  1. c) Montrer que chaque sommet est visité au plus une fois et donner le coût en fonction du nombre de sommets $n$ et d'arêtes $m$.
  1. d) Expliquer comment adapter la fonction pour obtenir la distance minimale (en nombre d'arêtes) entre s et chaque sommet.
Résoudre

Détecter un cycle

Un cycle est une « boucle » dans le graphe. Pour en repérer un dans un graphe non orienté, on fait un parcours en profondeur en retenant, pour chaque sommet, son parent (le sommet d'où l'on vient).

Pourquoi ? Ce voisin déjà visité est relié au départ par un chemin du parcours ; l'arête qu'on vient de trouver referme une boucle. Le parent, lui, est toujours déjà visité : l'arête qui y mène est celle qu'on vient de parcourir, elle ne prouve rien.

def a_cycle(g, u, parent, visites):

    visites.add(u)

    for v in g[u]:

        if v not in visites:

            if a_cycle(g, v, u, visites):

                return True

        elif v != parent:

            return True

    return False
Entraîne-toi★★☆Pas commencé
On souhaite détecter un cycle dans un graphe non orienté à l'aide d'un parcours en profondeur. a) Expliquer le principe : que signifie rencontrer un sommet déjà visité…

On souhaite détecter un cycle dans un graphe non orienté à l'aide d'un parcours en profondeur.

  1. a) Expliquer le principe : que signifie rencontrer un sommet déjà visité qui n'est pas le parent du sommet courant ?
  1. b) Appliquer ce principe au graphe d'arêtes 1–2, 2–3, 3–4, 4–2, 4–5.
  1. c) Le graphe d'arêtes 1–2, 2–3, 2–4, 4–5 possède-t-il un cycle ? Justifier.
Résoudre

Dépendances, interblocage et routage

Les graphes orientés modélisent des dépendances : un arc $X \to Y$ signifie « $X$ avant $Y$ ». Un ordre valide existe seulement si le graphe n'a pas de cycle orienté. Un cycle de dépendances est la même situation qu'un interblocage (deadlock) : chaque processus attend une ressource détenue par le suivant, et personne ne peut avancer. On le détecte en cherchant un cycle par un parcours en profondeur.

Les graphes pondérés (chaque arête porte un coût) modélisent les réseaux. Les routeurs choisissent la route d'un paquet selon un protocole :

  • RIP compte le nombre de sauts (routeurs traversés), sans tenir compte du débit ;

  • OSPF minimise la somme des coûts des liaisons, c'est-à-dire un plus court chemin dans un graphe pondéré.

L'algorithme de Dijkstra calcule ces plus courts chemins depuis un sommet de départ.

Entraîne-toi★★★Pas commencé
Un graphe orienté représente des dépendances entre modules d'un programme : un arc $X \to Y$ signifie que $X$ doit être chargé avant $Y$. Les arcs sont : M1→M2, M1→M3,…

Un graphe orienté représente des dépendances entre modules d'un programme : un arc $X \to Y$ signifie que $X$ doit être chargé avant $Y$. Les arcs sont : M1→M2, M1→M3, M2→M4, M3→M4, M4→M5.

  1. a) Donner un ordre de chargement valide.
  1. b) On ajoute l'arc M5→M2. Montrer qu'il n'existe plus d'ordre valide.
  1. c) Expliquer en quoi cette situation est analogue à un interblocage entre processus, et comment détecter ce problème par un parcours de graphe.
Résoudre
Entraîne-toi★★★Pas commencé
Dans un réseau, les routeurs R1 à R5 sont reliés ainsi, avec le coût de chaque liaison (OSPF) : R1–R2 : 1 ; R1–R3 : 4 ; R2–R3 : 2 ; R2–R4 : 7 ; R3–R5 : 3 ; R4–R5 : 1. a)…

Dans un réseau, les routeurs R1 à R5 sont reliés ainsi, avec le coût de chaque liaison (OSPF) : R1–R2 : 1 ; R1–R3 : 4 ; R2–R3 : 2 ; R2–R4 : 7 ; R3–R5 : 3 ; R4–R5 : 1.

  1. a) Déterminer le chemin de coût minimal de R1 à R4 en détaillant les étapes de l'algorithme de Dijkstra.
  1. b) Expliquer en quoi RIP (qui compte le nombre de sauts) choisirait éventuellement un autre chemin ; donner ce chemin.
  1. c) Expliquer le lien entre protocole de routage et recherche d'un chemin dans un graphe pondéré.
Résoudre

Vers le début : l'application de métro cherche un chemin dans un graphe dont les sommets sont les stations ; en comptant les stations, elle utilise un parcours en largeur, et avec les durées de trajet, un algorithme de type Dijkstra.

L'essentiel

  • Un graphe est défini par des sommets et des arêtes (non orienté) ou des arcs (orienté) ; le degré compte les arêtes d'un sommet.

  • Représentations : matrice d'adjacence (symétrique si non orienté), listes de successeurs/prédécesseurs ou de voisins (dictionnaire Python).

  • Parcours en largeur (file) : sommets par distance croissante, donne un plus court chemin en nombre d'arêtes ; en profondeur (pile ou récursivité). Chaque sommet est visité une fois : coût en $O(n+m)$.

  • Un cycle apparaît quand on rencontre un sommet déjà visité qui n'est pas le parent ; un cycle de dépendances rend tout ordre impossible (comme un interblocage).

  • RIP minimise le nombre de sauts, OSPF le coût total ; Dijkstra calcule les plus courts chemins dans un graphe pondéré.

Voir tous les exercices de cette partie