Chapitre 516 minIntermédiaire
Niveaux d'un graphe sans circuit et arborescence
Par Josselyn C., prof de BTS SIO. Mis à jour le .
À la fin de ce chapitre, tu sauras
- déterminer les niveaux d'un graphe sans circuit ;
- détecter un circuit pendant la recherche ;
- dessiner un graphe ordonné par niveaux ;
- utiliser les niveaux pour trouver un chemin hamiltonien ;
- reconnaître une arborescence.
5 exercices corrigés2 schémas
Dans un graphe sans circuit, le niveau d'un sommet est la longueur du plus long chemin qui arrive à ce sommet : les sommets sans prédécesseur sont au niveau 0, ceux qui n'ont que des prédécesseurs de niveau 0 au niveau 1, et ainsi de suite. Ranger les sommets par niveaux permet de dessiner le graphe proprement, de gauche à droite, avec toutes les flèches dans le même sens, et donne l'ordre dans lequel traiter des tâches qui dépendent les unes des autres. Ce chapitre donne la méthode pour déterminer les niveaux, la représentation par niveaux, le lien avec les chemins hamiltoniens, puis la notion d'arborescence.
Pourquoi « sans circuit » ?
Les niveaux n'existent que si le graphe n'a aucun circuit. Dans un circuit comme (A, B, A), A devrait être après B et B après A : impossible de les ranger. Un graphe sans circuit, en revanche, peut toujours être rangé : il a au moins une source (un sommet sans prédécesseur), sinon on pourrait remonter de prédécesseur en prédécesseur indéfiniment et on finirait par repasser par un sommet, c'est-à-dire par trouver un circuit.
Le graphe G du ticket a des circuits : il n'a pas de niveaux. Les exemples de ce chapitre sont des graphes de dépendances, sans circuit par nature.
Définition du niveau d'un sommet
Dans un graphe sans circuit :
- les sommets sans prédécesseur (les sources) sont de niveau 0 ;
- un sommet est de niveau k quand le plus long chemin qui arrive à lui, en partant d'une source, a pour longueur k.
Concrètement, on ne calcule pas les plus longs chemins : on « épluche » le graphe. On retire les sommets de niveau 0 ; dans ce qui reste, les nouveaux sommets sans prédécesseur sont de niveau 1 ; on les retire à leur tour ; les nouveaux sommets sans prédécesseur sont de niveau 2, etc.
La méthode du tableau des prédécesseurs
Exemple : un pipeline d'intégration continue. À chaque modification du code d'une application, un serveur exécute ces étapes :
| Étape | Description | Prédécesseurs |
|---|---|---|
| A | récupérer le code | — |
| B | installer les dépendances | A |
| C | compiler | B |
| D | lancer les tests unitaires | C |
| E | analyser la qualité du code | B |
| F | construire l'image Docker | C |
| G | déployer | D, E, F |
On dresse un tableau : une ligne par sommet avec ses prédécesseurs, puis une colonne par niveau. À chaque étape, on raye les sommets déjà classés dans les listes de prédécesseurs ; les sommets dont la liste est vide entrent dans le niveau suivant.
| Sommet | Prédécesseurs | Niveau 0 | Niveau 1 | Niveau 2 | Niveau 3 | Niveau 4 |
|---|---|---|---|---|---|---|
| A | — | A | ||||
| B | A | B | ||||
| C | B | C | ||||
| D | C | D | ||||
| E | B | E | ||||
| F | C | F | ||||
| G | D, E, F | G |
Le raisonnement, étape par étape :
- Niveau 0 : seul A n'a pas de prédécesseur. N₀ = {A}.
- On raye A. B n'a plus de prédécesseur non classé. N₁ = {B}.
- On raye B. C et E n'ont plus de prédécesseur non classé. N₂ = {C, E}.
- On raye C et E. D et F sont libres ; G attend encore D et F. N₃ = {D, F}.
- On raye D et F. N₄ = {G}.
Tous les sommets sont classés : le graphe est sans circuit, et ses niveaux sont N₀ = {A}, N₁ = {B}, N₂ = {C, E}, N₃ = {D, F}, N₄ = {G}.
La représentation par niveaux
On dessine les niveaux en colonnes, de gauche à droite (ou en lignes, de haut en bas), et on place les arcs. Toutes les flèches vont alors d'un niveau vers un niveau plus grand : le dessin se lit dans un seul sens, sans croisement inutile.
Ce que la représentation apprend. Deux sommets d'un même niveau ne sont jamais reliés par un arc : ils ne dépendent pas l'un de l'autre. Dans le pipeline, l'analyse de qualité (E) peut tourner en parallèle de la compilation (C), et les tests (D) en parallèle de la construction de l'image (F). Le nombre de niveaux, 5, est le nombre minimal de « vagues » successives pour tout exécuter, même avec autant de serveurs qu'on veut.
Lire les sommets niveau par niveau (A, B, C, E, D, F, G) donne un ordre dans lequel chaque sommet vient après tous ses prédécesseurs. Les informaticiens appellent cela un tri topologique : c'est l'ordre dans lequel un gestionnaire de paquets installe les dépendances, ou dans lequel un outil comme make ou un orchestrateur de pipeline lance ses tâches.
Niveaux et chemin hamiltonien
Dans un graphe sans circuit, le niveau maximal est la longueur du plus long chemin du graphe. Un chemin hamiltonien, qui passe par les n sommets, a n − 1 arcs : s'il existe, le plus long chemin a n − 1 arcs, il y a donc n niveaux (0 à n − 1) pour n sommets, soit un sommet par niveau. Inversement, s'il y a un sommet par niveau, le sommet du dernier niveau est au bout d'un chemin de longueur n − 1, qui passe forcément par les n sommets.
Le pipeline a deux sommets aux niveaux 2 et 3 : il n'a pas de chemin hamiltonien. On ne peut pas enchaîner toutes les étapes en une seule file en suivant les arcs : C et E, par exemple, ne sont reliés dans aucun sens.
Exemple avec chemin hamiltonien. Le graphe de sommets A, B, C, D, E et de prédécesseurs Γ⁻(A) = {C}, Γ⁻(B) = ∅, Γ⁻(C) = {B}, Γ⁻(D) = {A, C}, Γ⁻(E) = {B, D} a pour niveaux N₀ = {B}, N₁ = {C}, N₂ = {A}, N₃ = {D}, N₄ = {E}. Un sommet par niveau : le chemin hamiltonien est (B, C, A, D, E), et on vérifie que les arcs B → C, C → A, A → D et D → E existent.
Les arborescences
Une arborescence est un graphe orienté qui a un sommet particulier, la racine, tel que :
- la racine n'a aucun prédécesseur ;
- tout autre sommet a exactement un prédécesseur ;
- tout sommet peut être atteint depuis la racine.
De façon équivalente, il existe un et un seul chemin de la racine vers chaque autre sommet. Une arborescence n'a donc pas de circuit, et ses niveaux sont simplement les distances à la racine : la racine au niveau 0, ses successeurs au niveau 1, etc.
Le vocabulaire vient de la généalogie et de la botanique : le prédécesseur d'un sommet est son père (ou parent), ses successeurs sont ses fils (ou enfants), et les sommets sans successeur sont les feuilles.
L'arborescence des fichiers est l'exemple que tu manipules tous les jours : le chemin absolu d'un fichier, comme /var/log, est exactement l'unique chemin de la racine vers ce sommet. On rencontre aussi des arborescences dans :
- le DNS : la racine, puis les domaines de premier niveau (
fr,com), puis les domaines (exemple.fr), puis les sous-domaines ; - l'Active Directory : le domaine, ses unités d'organisation, leurs sous-unités et leurs objets ;
- une page web : l'arbre DOM des balises HTML, chaque balise ayant une seule balise parente ;
- un organigramme hiérarchique, où chacun a un seul supérieur direct.
En Python : calculer les niveaux
def niveaux(predecesseurs):
"""Renvoie la liste des niveaux, ou None si le graphe a un circuit."""
restants = dict(predecesseurs) # sommet -> liste de prédécesseurs
classes = set()
resultat = []
while restants:
niveau = sorted(x for x, preds in restants.items()
if all(p in classes for p in preds))
if not niveau: # plus aucun sommet libre : circuit
return None
resultat.append(niveau)
classes.update(niveau)
for x in niveau:
del restants[x]
return resultat
pipeline = {"A": [], "B": ["A"], "C": ["B"], "D": ["C"],
"E": ["B"], "F": ["C"], "G": ["D", "E", "F"]}
print(niveaux(pipeline))
# [['A'], ['B'], ['C', 'E'], ['D', 'F'], ['G']]
C'est la traduction directe de la méthode du tableau. Le cas None correspond à l'étape où plus aucun sommet n'est libre : un circuit bloque le classement.
Exercices
Exercice 1 sur 5 Déterminer des niveaux Réussi À revoir
Un graphe a pour tableau des prédécesseurs :
| Sommet | A | B | C | D | E | F | G | H |
|---|---|---|---|---|---|---|---|---|
| Prédécesseurs | — | A, D | B | — | A, D | C, E | E | F, G |
- Détermine les niveaux de ce graphe.
- Représente-le ordonné par niveaux.
- A-t-il un chemin hamiltonien ?
Voir la réponseMasquer la réponse de l'exercice 1
- N₀ = {A, D} (pas de prédécesseur). On raye A et D : B et E sont libres, N₁ = {B, E}. On raye B et E : C et G sont libres (F attend C), N₂ = {C, G}. On raye C et G : N₃ = {F}. On raye F : N₄ = {H}.
| Sommet | Prédécesseurs | N₀ | N₁ | N₂ | N₃ | N₄ |
|---|---|---|---|---|---|---|
| A | — | A | ||||
| B | A, D | B | ||||
| C | B | C | ||||
| D | — | D | ||||
| E | A, D | E | ||||
| F | C, E | F | ||||
| G | E | G | ||||
| H | F, G | H |
- Colonnes de gauche à droite : {A, D}, {B, E}, {C, G}, {F}, {H}. Arcs : A → B, A → E, D → B, D → E, B → C, E → F, E → G, C → F, F → H, G → H, tous dirigés vers la droite.
- Les niveaux 0, 1 et 2 contiennent deux sommets : pas de chemin hamiltonien. (Il y a d'ailleurs deux sources, A et D, ce qui suffisait à conclure.)
Tu avais trouvé ?
Exercice 2 sur 5 Un circuit caché Réussi À revoir
Un graphe a pour tableau des prédécesseurs : Γ⁻(A) = ∅, Γ⁻(B) = {A, E}, Γ⁻(C) = {B}, Γ⁻(D) = {C}, Γ⁻(E) = {D}, Γ⁻(F) = {C}.
- Commence la recherche des niveaux. Que constates-tu ?
- Trouve le circuit.
Voir la réponseMasquer la réponse de l'exercice 2
- N₀ = {A}. On raye A : B a encore le prédécesseur E, non classé ; C, D, E, F ont aussi des prédécesseurs non classés. Aucun sommet n'est libre : la recherche s'arrête, le graphe a un circuit, il n'a pas de niveaux.
- On remonte les prédécesseurs : B ← E ← D ← C ← B. Le circuit est (B, C, D, E, B).
Tu avais trouvé ?
Exercice 3 sur 5 Niveaux et chemin hamiltonien Réussi À revoir
Un graphe a pour matrice d'adjacence (ordre A, B, C, D, E) :
| A | B | C | D | E | |
|---|---|---|---|---|---|
| A | 0 | 0 | 0 | 1 | 0 |
| B | 0 | 0 | 1 | 0 | 1 |
| C | 1 | 0 | 0 | 1 | 0 |
| D | 0 | 0 | 0 | 0 | 1 |
| E | 0 | 0 | 0 | 0 | 0 |
- Donne le tableau des prédécesseurs, puis les niveaux.
- Justifie que le graphe a un chemin hamiltonien et donne-le.
Voir la réponseMasquer la réponse de l'exercice 3
- On lit les colonnes : Γ⁻(A) = {C}, Γ⁻(B) = ∅, Γ⁻(C) = {B}, Γ⁻(D) = {A, C}, Γ⁻(E) = {B, D}. Niveaux : N₀ = {B}, N₁ = {C}, N₂ = {A}, N₃ = {D}, N₄ = {E}.
- Le graphe est sans circuit (les niveaux existent) et chaque niveau contient un seul sommet : il a un chemin hamiltonien, qui parcourt les niveaux dans l'ordre, (B, C, A, D, E). Vérification dans la matrice : (B, C), (C, A), (A, D) et (D, E) valent bien 1.
Tu avais trouvé ?
Exercice 4 sur 5 Arborescence ou pas ? Réussi À revoir
Pour chaque graphe, dis s'il s'agit d'une arborescence et, si oui, donne sa racine et ses feuilles.
- Arcs (A, B), (A, C), (B, D), (B, E), (C, F).
- Arcs (A, B), (A, C), (B, D), (C, D).
- Arcs (R, S), (S, T), (T, R), (R, U).
- Arcs (fr, gouv.fr), (fr, exemple.fr), (exemple.fr, www.exemple.fr), (exemple.fr, mail.exemple.fr).
Voir la réponseMasquer la réponse de l'exercice 4
- Oui. A n'a pas de prédécesseur, chaque autre sommet en a exactement un, et tous sont atteints depuis A. Racine A, feuilles D, E, F.
- Non : D a deux prédécesseurs, B et C. Il y a deux chemins de A vers D.
- Non : (R, S, T, R) est un circuit, et aucun sommet n'est sans prédécesseur.
- Oui : c'est un morceau de l'arborescence du DNS. Racine fr, feuilles gouv.fr, www.exemple.fr, mail.exemple.fr. (Dans le DNS réel, la racine de tout l'arbre est la zone racine, notée « . ».)
Tu avais trouvé ?
Exercice 5 sur 5 Ordre d'installation Réussi À revoir
Un logiciel L nécessite les bibliothèques P et Q ; P nécessite R et S ; Q nécessite S ; S nécessite T ; R et T ne nécessitent rien.
- Construis le graphe « x doit être installé avant y » et détermine ses niveaux.
- Propose un ordre d'installation, puis le nombre minimal de vagues si l'on peut installer plusieurs bibliothèques en parallèle.
Voir la réponseMasquer la réponse de l'exercice 5
- Arcs (« avant ») : R → P, S → P, S → Q, T → S, P → L, Q → L. Prédécesseurs : R : ∅ ; T : ∅ ; S : {T} ; P : {R, S} ; Q : {S} ; L : {P, Q}. Niveaux : N₀ = {R, T}, N₁ = {S}, N₂ = {P, Q}, N₃ = {L}.
- Un ordre possible, niveau par niveau : R, T, S, P, Q, L. Avec du parallélisme, il faut 4 vagues : {R, T}, puis S, puis {P, Q}, puis L, une par niveau.
Tu avais trouvé ?