Graphes orientés, DAG et ordonnancement
Ce que ce chapitre apporte 6 points
- Manipuler successeurs, prédécesseurs, degré entrant et degré sortant.
- Distinguer connexité et forte connexité, et trouver les composantes fortement connexes d'un graphe orienté.
- Reconnaître un DAG et dire pourquoi c'est la bonne structure pour des dépendances.
- Produire un tri topologique par l'algorithme de Kahn.
- Détecter un circuit par le tri topologique, et l'exhiber par un parcours en profondeur.
- Calculer les dates au plus tôt et au plus tard d'un projet, ses marges et son chemin critique.
Ce que les flèches changent
Dans un graphe orienté, chaque sommet a deux voisinages distincts, et la plupart des erreurs viennent de leur confusion.
Les successeurs de sont les sommets tels que l'arc existe. Les prédécesseurs sont les tels que existe.
Le degré sortant compte les successeurs, le degré entrant compte les prédécesseurs.
Sur un graphe orienté, la somme des degrés sortants vaut le nombre d'arcs, et la somme des degrés entrants aussi. Il n'y a pas de facteur 2 : chaque arc n'est compté qu'une fois de chaque côté.
A à F, mais jamais de F à A. Dire « ces deux sommets sont reliés » n'a plus de sens : il faut préciser le sens.Les parcours du chapitre 3 restent valables en suivant les arcs, mais leur interprétation change. Un BFS orienté donne les sommets accessibles depuis le départ, ce qui n'est pas la composante connexe.
La détection de cycle du chapitre 3, elle, ne s'applique plus telle quelle. Elle conclut dès qu'une arête mène à un sommet déjà vu ; or, sur ce graphe,
C mène à D et à E, qui mènent tous deux à F. En arrivant à F pour la seconde fois, elle croit avoir bouclé et annonce un circuit, alors qu'il n'y en a aucun : deux chemins qui se rejoignent ne forment pas un circuit. La méthode juste, qui distingue les sommets en cours d'exploration de ceux déjà terminés, est donnée plus bas, avec le tri topologique.
Un graphe orienté est fortement connexe si, pour toute paire , il existe un chemin de vers et un chemin de vers .
Il est faiblement connexe si le graphe non orienté obtenu en effaçant les flèches est connexe.
Un plan de circulation en sens uniques en donne l'exemple le plus parlant.
Le dessin est d'un seul tenant, mais un véhicule parti du port n'a aucun moyen de rejoindre la gare : le graphe est faiblement connexe, pas fortement. Pour tester la forte connexité, deux parcours suffisent, depuis un même sommet quelconque : l'un en suivant les arcs, l'autre en les remontant, c'est-à-dire sur le graphe inverse du chapitre 2. Le graphe est fortement connexe si et seulement si chacun des deux atteint tous les sommets : tout sommet peut alors rejoindre le sommet de départ, puis n'importe quel autre. Ici, depuis Gare, on atteint tout en suivant les arcs, mais en les remontant on n'atteint ni Port ni Usine. Les groupes de lieux qui communiquent dans les deux sens, coloriés sur la figure, sont les composantes fortement connexes de la fin du chapitre.
Les DAG
Un DAG (directed acyclic graph), ou graphe orienté sans circuit, est un graphe orienté ne contenant aucun circuit : on ne peut jamais revenir à son point de départ en suivant les flèches.
C'est la structure des dépendances, et elle est partout : tâches d'un projet, prérequis d'un cours, modules d'un programme, cellules d'un tableur, commits d'un dépôt Git, étapes d'une chaîne d'intégration.
Un circuit dans un graphe de dépendances est une contradiction. Si A doit être fait avant B, B avant C, et C avant A, aucun ordre n'existe. Ce n'est pas un problème d'algorithme, c'est un problème d'énoncé.
C'est pourquoi la détection de circuit n'est pas un raffinement : c'est la première chose à faire sur un graphe de dépendances. Les outils réels le font, et c'est exactement ce que signale un gestionnaire de paquets qui parle de dépendance circulaire.
Le tri topologique
Un tri topologique est un ordre linéaire des sommets tel que, pour tout arc , apparaisse avant .
Un tel ordre existe si et seulement si le graphe est un DAG.
Un tri topologique existe…
L'algorithme de Kahn est le plus lisible : on prend une source, c'est-à-dire un sommet sans prédécesseur, celui dont la colonne est vide dans la matrice d'adjacence du chapitre 2 ; on la sort, on la retire du graphe, et l'on recommence. Pour savoir qui devient source, il suffit de tenir à jour le degré entrant de chaque sommet : retirer un sommet fait baisser d'un celui de chacun de ses successeurs, et un sommet dont le degré tombe à 0 entre dans une file des sommets prêts.
La figure reprend le graphe du début du chapitre, lu comme six tâches A à F et leurs contraintes : C <- A, B se lit « C attend A et B », c'est-à-dire les arcs A -> C et B -> C. Chaque tâche porte, sous son nom, son degré entrant.
- A
- B
C, D et E sont prêtes en même temps.Cliquer ensuite
C et lui faire attendre aussi F, puis dérouler de nouveau : Kahn sort A et B, et s'arrête. Les quatre tâches qui restent attendent chacune une autre tâche restante, et la figure épaissit le circuit qu'elles contiennent, C, D, F.
L'ordre obtenu, A B C D E F, respecte toutes les flèches. Il n'est pas unique : B A C E D F conviendrait aussi. Quand plusieurs sommets sont prêts en même temps, on peut prendre n'importe lequel, et c'est même une information utile : ces tâches-là peuvent être menées en parallèle.
Aucun calcul supplémentaire n'est nécessaire. Le nombre de sommets sortis, comparé à , est le test.
Exhiber le circuit
Kahn dit qu'il y a un circuit, pas lequel. Sur le graphe ci-dessous, les arcs A -> B, B -> C, C -> D, D -> B et A -> E, il sort A, puis E, et s'arrête : les trois sommets restants gardent chacun un prédécesseur. La figure s'ouvre sur cet arrêt ; l'arc D -> B, qui referme le circuit, est dessiné en retour.
- vide
B et retirer la contrainte qui lui fait attendre D : Kahn va alors jusqu'au bout, dans l'ordre A B E C D. Ici, retirer une seule contrainte du circuit suffit, n'importe laquelle : toute la question est de savoir laquelle est fausse dans l'énoncé.
Ce circuit épaissi, Kahn ne le fournit pas : la figure le cherche par la méthode qui suit.
Pour dire quel circuit, et donc quelle contrainte retirer, on revient au parcours en profondeur du chapitre 3, avec une précision : chaque sommet passe par trois états. Il est neuf tant qu'on ne l'a pas atteint, en cours tant qu'on explore ce qui part de lui, terminé quand tout ce qui part de lui a été exploré. Les sommets en cours forment exactement le chemin qu'on est en train de descendre. Un arc vers un sommet en cours referme donc ce chemin sur lui-même : c'est un circuit, et la liste des sommets en cours le donne. Un arc vers un sommet terminé, au contraire, rejoint seulement un chemin déjà exploré, comme le second des deux arcs qui mènent à F sur le premier graphe du chapitre : ce n'est pas un circuit.
Sur le graphe du chapitre, la fonction ne trouve rien, à juste titre. Sur un planning où la recette a été placée par erreur avant la base de données, elle nomme les trois tâches du circuit : c'est l'une de ces trois contraintes qu'il faut retirer.
Ordonnancer un projet
Le tri topologique dit dans quel ordre faire les choses. Avec des durées, il dit aussi quand, et surtout combien de temps prendra l'ensemble.
La date de début au plus tôt d'une tâche est la plus grande des dates de fin de ses prédécesseurs :
Une tâche sans prédécesseur commence à 0.
Ce calcul se fait en une seule passe dans l'ordre topologique : quand on arrive à un sommet, tous ses prédécesseurs sont déjà calculés. C'est la raison profonde pour laquelle on trie d'abord.
La date de début au plus tard d'une tâche est la dernière date à laquelle elle peut commencer sans retarder le projet. Une tâche doit être finie quand le plus pressé de ses successeurs doit commencer :
Une tâche sans successeur a pour date au plus tard la durée totale du projet moins sa propre durée. Ce calcul se fait en une seconde passe, en sens inverse de l'ordre topologique : quand on arrive à une tâche, tous ses successeurs sont déjà calculés.
La marge d'une tâche est l'écart entre sa date au plus tard et sa date au plus tôt : le retard qu'elle peut prendre sans retarder le projet.
Le chemin critique est le chemin le plus long, en durée cumulée, du début à la fin du projet. Sa durée est la durée totale minimale du projet.
Une tâche est critique quand sa marge est nulle : date au plus tôt et date au plus tard coïncident. La retarder d'un jour retarde tout le projet d'un jour.
Le calcul ne sert pas qu'aux plannings. Une chaîne d'intégration continue est le même graphe, avec des travaux à la place des tâches, et son chemin critique dit où une optimisation fera gagner du temps et où elle n'en fera gagner aucun.
Voici un projet de sept tâches, dont les durées sont données en jours. Dans la figure, chaque tâche porte sa durée à droite de son nom et, dessous, ses débuts au plus tôt et au plus tard, puis sa marge ; un arc va d'une tâche vers une tâche qui l'attend. Rien n'y est dessiné à la main : les dates, les marges et le chemin critique sont calculés, et le restent quand on change une durée ou une contrainte.
- Analyse : du jour 0 au jour 5, critique.
- Modele : du jour 5 au jour 13, critique.
- Maquette : du jour 5 au jour 8, marge de 9 jours, fin au plus tard au jour 17.
- Base : du jour 13 au jour 19, critique.
- Interface : du jour 8 au jour 10, marge de 9 jours, fin au plus tard au jour 19.
- Service : du jour 19 au jour 26, critique.
- Recette : du jour 26 au jour 30, critique.
Barre pleine : la tâche placée à sa date au plus tôt, en vert si elle est critique. Hachures : sa marge, le retard qu'elle peut prendre sans repousser la fin du projet.
Service : sa date au plus tôt, 19, est la plus tardive des fins de Base (19) et d'Interface (10), pas leur somme.Cliquer ensuite
Maquette et l'allonger d'un jour à la fois, en prédisant avant chaque clic la durée du projet. Tant que l'allongement reste dans sa marge de 9 jours, la fin du projet ne bouge pas : à 12 jours, la branche de la maquette devient seulement critique à son tour. Un jour de plus, et le projet passe à 31 jours, le chemin critique ayant basculé sur cette branche.Enfin, cliquer
Maquette et lui faire attendre aussi Service : toutes les dates disparaissent, parce que ces contraintes forment un circuit, et aucun planning ne peut les satisfaire.
Le chemin en gras est le plus long du début à la fin du projet : c'est le chemin critique, Analyse, Modele, Base, Service, Recette, pour 30 jours. La branche par la maquette et l'interface est plus courte, elle dispose donc d'une marge, hachurée sur le diagramme de Gantt. Le tableau reprend toutes les dates, comptées en jours depuis le début du projet.
| Tâche | Durée | Début au plus tôt | Début au plus tard | Marge |
|---|---|---|---|---|
| Analyse | 5 | 0 | 0 | 0 |
| Maquette | 3 | 5 | 14 | 9 |
| Modele | 8 | 5 | 5 | 0 |
| Interface | 2 | 8 | 17 | 9 |
| Base | 6 | 13 | 13 | 0 |
| Service | 7 | 19 | 19 | 0 |
| Recette | 4 | 26 | 26 | 0 |
La colonne « au plus tôt » se remplit de haut en bas, avec le maximum : Service attend Base, finie au jour , et Interface, finie au jour ; il commence donc au jour 19. La colonne « au plus tard » se remplit de bas en haut, avec le minimum : Recette doit commencer au jour , donc Service au jour ; Interface, qui dure 2 jours, peut alors commencer jusqu'au jour 17, et Maquette, qui doit être finie avant, jusqu'au jour 14, soit 9 jours après sa date au plus tôt.
Un plus long chemin, calculé comme un plus court. Porter la durée de chaque tâche sur les arcs qui en partent (l'arc de Modele vers Base pèse alors 8, et un sommet Fin ajouté après Recette reçoit un arc de poids 4) fait du graphe des tâches un graphe pondéré ordinaire. La formule de la date au plus tôt devient alors le relâchement du chapitre sur les plus courts chemins, où le minimum est remplacé par un maximum : on cherche le chemin le plus long, et non le plus court.
Sur un graphe sans circuit, une seule passe dans l'ordre topologique suffit, exactement comme pour les plus courts chemins sur un tel graphe. Sur un graphe quelconque, en revanche, chercher le plus long chemin qui ne repasse par aucun sommet est un problème difficile, comme la coloration du chapitre suivant : c'est l'absence de circuit qui rend l'ordonnancement facile.
Cette façon de faire, où chaque tâche est un sommet et chaque arc une contrainte d'antériorité, s'appelle la méthode des potentiels. La méthode PERT, plus ancienne, place au contraire les tâches sur les arcs ; les deux donnent les mêmes dates. La marge calculée ici est la marge totale. La marge libre est le retard qu'une tâche peut prendre sans retarder le début d'aucun de ses successeurs : Maquette a 9 jours de marge totale mais aucune marge libre, puisque tout retard de Maquette repousse Interface ; Interface, elle, a 9 jours de marge libre.
L'erreur qui consiste à additionner donne des projets bien trop longs : 40 jours au lieu de 30 sur le projet ci-dessus. Elle se repère à ce que deux tâches ne se déroulent jamais en même temps.
1.L'algorithme de Kahn s'arrête avec trois sommets non sortis. Que conclure ?
2.Une tâche a trois prédécesseurs. Sa date de début au plus tôt est…
Les composantes fortement connexes
Le plan de sens uniques du début du chapitre se découpait en deux groupes de lieux qui communiquent dans les deux sens. Ces groupes portent un nom, et se calculent aussi vite qu'un parcours.
Une composante fortement connexe est un groupe maximal de sommets qui s'atteignent tous mutuellement : on ne peut lui ajouter aucun sommet sans perdre cette propriété. Tout sommet appartient à exactement une composante, éventuellement réduite à lui seul.
En contractant chaque composante en un seul sommet, on obtient le graphe des composantes. Il est toujours sans circuit : si un circuit reliait deux composantes, leurs sommets s'atteindraient mutuellement, et elles n'en feraient qu'une. Tout graphe orienté est donc un DAG de composantes fortement connexes.
Sur le plan de sens uniques, le centre (Gare, Mairie, Marché) et le port (Port, Usine) sont les deux composantes, et le graphe des composantes se réduit à un seul arc, du centre vers le port. À l'autre extrême, dans un DAG, aucun sommet n'en atteint un autre qui puisse revenir vers lui : chaque sommet est seul dans sa composante. Une composante de plus d'un sommet contient donc toujours un circuit. Dans un graphe de dépendances, c'est un paquet de modules qui dépendent les uns des autres en boucle, et qu'il faudra traiter ensemble.
Deux parcours en profondeur : l'algorithme de Kosaraju
Le premier parcours en profondeur, sur le graphe, note l'ordre dans lequel les sommets se terminent : un sommet est terminé quand tout ce qui part de lui a été exploré. Le second parcours se fait sur le graphe inverse, où chaque arc est retourné, et repart des sommets dans l'ordre inverse de fin, du dernier terminé au premier. Chaque parcours lancé dans cette seconde phase ramasse exactement une composante.
La raison tient en deux phrases. Le sommet terminé en dernier appartient à une composante de tête, dans laquelle aucun arc n'entre depuis une autre composante ; en remontant les arcs depuis lui, on ne peut donc pas sortir de sa composante, et l'on ramasse tous ses sommets, et eux seuls. On la met de côté, et parmi les sommets restants, le dernier terminé joue le même rôle.
Sur le plan de sens uniques, le premier parcours part de Gare et descend Mairie, Marché, Port, Usine : les sommets se terminent dans l'ordre Usine, Port, Marché, Mairie, Gare. Le second parcours part donc de Gare, en remontant les arcs : il atteint Marché, puis Mairie, et s'arrête, car aucun arc ne remonte du port vers le centre. C'est la première composante. Le sommet suivant qui n'a pas encore été ramassé, dans l'ordre inverse de fin, est Port : en remontant, il atteint Usine. C'est la seconde.
Les composantes sortent dans l'ordre d'un tri topologique du graphe des composantes : le centre avant le port, et, sur le DAG du chapitre, chaque sommet seul, dans l'ordre B A C E D F, qui est bien un tri topologique : c'est celui que la section sur Kahn citait en exemple. L'algorithme de Tarjan obtient les mêmes composantes en un seul parcours en profondeur, au prix d'une comptabilité plus fine : il numérote les sommets dans l'ordre où il les découvre et retient, pour chacun, le plus petit numéro qu'il permet d'atteindre parmi les sommets pas encore rangés dans une composante. Il rend les composantes dans l'ordre inverse d'un tri topologique du graphe des composantes. Les deux coûtent , comme un simple parcours. Le parcours Logique et solveurs SAT s'en sert pour décider en temps linéaire si un problème 2-SAT a une solution : voir les fragments faciles.
Mettre en pratique
Quand un tri topologique existe-t-il, et que faire s'il n'existe pas ?
Il existe si et seulement si le graphe est un DAG, c'est-à-dire sans circuit.
S'il n'existe pas, l'algorithme de Kahn s'arrête avec des sommets non sortis : ceux-là appartiennent à un circuit ou en dépendent.
Ce n'est pas un défaut de l'algorithme mais un défaut des données : un ensemble de contraintes contradictoires, du type « A avant B, B avant C, C avant A ». Il faut retirer une contrainte, pas changer d'algorithme.
Le tri topologique est-il unique ?
Non, sauf cas particulier.
Chaque fois que plusieurs sommets ont un degré entrant nul en même temps, n'importe lequel peut être choisi, ce qui donne des ordres différents, tous valides.
L'ordre est unique si et seulement si, à chaque étape, exactement un sommet est disponible : cela revient à dire que le DAG contient un chemin hamiltonien, un chemin passant par tous les sommets.
Les sommets simultanément disponibles sont d'ailleurs une information précieuse : ce sont les tâches parallélisables.
Pourquoi calcule-t-on les dates au plus tôt dans l'ordre topologique ?
Parce que la date d'une tâche dépend de celles de tous ses prédécesseurs.
Dans l'ordre topologique, quand on arrive à un sommet, tous ses prédécesseurs ont déjà été traités : leur date est définitive, et une seule passe suffit.
Dans un autre ordre, il faudrait recalculer certaines dates plusieurs fois, jusqu'à ce que plus rien ne bouge, pour un résultat identique et un code plus compliqué.
Quelle différence entre connexe et fortement connexe ?
Faiblement connexe : en effaçant les flèches, on obtient un graphe connexe. Autrement dit, le dessin est d'un seul tenant.
Fortement connexe : pour toute paire , il existe un chemin de vers et un chemin de vers , en respectant les flèches.
Un DAG non trivial n'est jamais fortement connexe : s'il l'était, il contiendrait un circuit. Un graphe peut donc être d'un seul tenant sans que ses sommets communiquent dans les deux sens, comme le plan de sens uniques du chapitre, qui se découpe en deux composantes fortement connexes.
Une tâche du chemin critique prend un jour de retard. Que se passe-t-il ?
Le projet entier prend un jour de retard.
C'est la définition même du chemin critique : ses tâches n'ont aucune marge, donc tout retard s'y propage jusqu'à la fin.
À l'inverse, une tâche hors chemin critique dispose d'une marge égale à l'écart entre sa date au plus tard et sa date au plus tôt. Ce n'est pas l'écart avec la fin du projet : Maquette finit au plus tôt au jour 8, vingt-deux jours avant la fin, mais sa marge n'est que de 9 jours, parce qu'Interface puis Service l'attendent. Elle peut glisser dans cette limite sans conséquence ; au-delà, elle retarde tout le projet et le chemin critique change : portée de 3 à 13 jours sur la figure du chapitre, elle fait passer le projet à 31 jours.
Comment détecter un circuit dans un graphe orienté ?
Deux méthodes, également valables.
Par Kahn : on lance le tri topologique et on compare le nombre de sommets sortis à . S'il en manque, il y a un circuit. C'est gratuit puisqu'on veut souvent le tri de toute façon.
Par parcours en profondeur : on distingue les sommets en cours d'exploration de ceux déjà terminés. Rencontrer un arc vers un sommet en cours signale un circuit, et la liste des sommets en cours en donne les sommets ; un arc vers un sommet terminé ne signale rien. C'est la fonction trouver_circuit du chapitre.
La seconde méthode a l'avantage d'exhiber le circuit, ce qui est bien plus utile qu'un simple « il y en a un » dans un message d'erreur.
Chapitre révisé en octobre 2026 · Une erreur dans ce chapitre ?
Dans la bibliothèque d'exercices
Tri topologique, dates au plus tôt, marges et chemin critique.
- Les dates au plus tôtNiveau 3
- Le chemin critique et les margesNiveau 4
- Débogage : une file qui ne se remplit pasNiveau 3
- La dépendance circulaireNiveau 2
- Remettre le tri topologique dans l'ordreNiveau 2
- Le planning du chantierNiveau 3