Représenter un graphe en machine
Ce que ce chapitre apporte 5 points
- Construire une liste d'adjacence, une matrice d'adjacence et une liste d'arêtes à partir du même graphe.
- Donner le coût en mémoire et en temps de chaque opération courante sur chacune.
- Distinguer un graphe creux d'un graphe dense, et choisir la représentation en conséquence.
- Interpréter les puissances de la matrice d'adjacence.
- Adapter la représentation aux graphes orientés et pondérés.
Le même graphe, trois écritures
Prenons ce graphe et écrivons-le trois fois.
La liste d'adjacence donne, pour chaque sommet, la liste de ses voisins. En Python, c'est un dictionnaire qui associe à chaque sommet la liste de ses voisins, {sommet: [voisins]} : c'est la convention de tout ce parcours.
La matrice d'adjacence est un tableau où si l'arête existe, 0 sinon. Les opérations qu'on lui applique, produit et puissances, sont celles du chapitre de mathématiques sur les matrices.
La liste d'arêtes est la simple liste des paires.
| A | B | C | D | E | F | d | |
|---|---|---|---|---|---|---|---|
| A | 0 | 1 | 1 | 0 | 0 | 0 | 2 |
| B | 1 | 0 | 1 | 0 | 0 | 0 | 2 |
| C | 1 | 1 | 0 | 1 | 0 | 0 | 3 |
| D | 0 | 0 | 1 | 0 | 1 | 1 | 3 |
| E | 0 | 0 | 0 | 1 | 0 | 1 | 2 |
| F | 0 | 0 | 0 | 1 | 1 | 0 | 2 |
- A
- : {B, C}
- B
- : {A, C}
- C
- : {A, B, D}
- D
- : {C, E, F}
- E
- : {D, F}
- F
- : {D, E}
- (A, B)
- (A, C)
- (B, C)
- (C, D)
- (D, E)
- (D, F)
- (E, F)
Les trois écritures contiennent la même information, à un détail près : un sommet isolé, sans aucune arête, n'apparaît dans aucune ligne d'une liste d'arêtes, et il faut alors garder à part la liste des sommets. La matrice (une ligne de zéros) et la liste d'adjacence (une liste vide) le conservent. À cette réserve près, chacune se reconstruit à partir de n'importe laquelle des deux autres. Ce qui les sépare n'est pas ce qu'elles disent, mais ce qu'elles rendent facile.
La diagonale nulle. La case demande « le sommet est-il son propre voisin ». Un 1 sur la diagonale est une boucle ; un graphe simple n'en a aucune.
Les degrés, par une somme. La somme d'une ligne compte les voisins du sommet : c'est son degré, la colonne ajoutée à droite de la figure. Cette somme lit toute la ligne, soit cases ; la liste d'adjacence, elle, donne le degré sans rien lire, par la longueur de la liste des voisins,
len(adjacence[u]).
A[i][j] sans se demander lequel de ou est le plus grand vaut bien ce gâchis.Sur les très grands graphes, ce n'est pas ce facteur 2 qui pose problème, mais le lui-même : la section suivante le chiffre.
Ce que chacune coûte
Les coûts s'écrivent avec la notation , qui dit comment le temps ou la mémoire grandit avec la taille du graphe, sans compter de secondes : signifie « au plus proportionnel à », et , qu'emploie le chapitre suivant, « exactement de cet ordre, ni plus ni moins ». Le chapitre sur la complexité les définit ; ici, il suffit de lire un coût en comme un coût qui quadruple quand le nombre de sommets double, et un coût en comme un coût qui double quand les sommets et les arêtes doublent.
| Opération | Liste d'adjacence | Matrice | Liste d'arêtes |
|---|---|---|---|
| et sont-ils voisins ? | , en moyenne avec un set | ||
| Parcourir les voisins de | |||
| Degré de | , par len | ||
| Ajouter une arête | |||
| Supprimer une arête | , en moyenne avec un set | ||
| Mémoire |
Le d'un set est une moyenne : c'est une table de hachage, rapide en pratique, sans garantie dans le pire cas.
La ligne décisive est la deuxième. Presque tous les algorithmes de ce module passent leur temps à demander « qui sont les voisins de ce sommet ». Avec une liste d'adjacence, la réponse coûte le degré du sommet ; avec une matrice, elle coûte , qu'il y ait deux voisins ou aucun, car il faut lire toute la ligne.
Un graphe est creux quand est de l'ordre de , dense quand approche son maximum .
Comment choisir, en une règle. Les graphes réels sont presque toujours creux : réseaux sociaux, routes, dépendances entre modules, chevauchements de tâches. Chaque sommet n'a que quelques voisins, quelques centaines tout au plus, quel que soit le nombre total de sommets.
Pour un graphe creux, la liste d'adjacence gagne sur les deux tableaux à la fois : moins de mémoire et parcours plus rapide. C'est le choix par défaut.
La matrice ne se justifie que sur un graphe dense, sur de très petits graphes, ou quand on veut exploiter ses propriétés algébriques.
Matrice : cases. Même à un bit par case, cela fait plus de cent mille téraoctets. Impossible, et pas approximativement : impossible.
Liste d'adjacence : chaque compte range ses 200 relations, soit entrées. Chaque amitié y est écrite deux fois, une fois chez chacun des deux comptes : c'est bien pour amitiés, comme le veut le lemme des poignées de main. Environ un téraoctet et demi à huit octets par entrée, réparti sur un ensemble de machines. C'est ce que font les vrais systèmes.
Ce que la matrice sait faire toute seule
La matrice a un pouvoir que les deux autres représentations n'ont pas : elle se multiplie.
Le coefficient de donne le nombre de chemins de longueur exactement entre les sommets et .
Ces chemins peuvent repasser par un sommet ou par une arête : C A C D est l'un des cinq chemins de longueur 3 qui vont de C à D.
La raison est simple à voir sur . Le coefficient vaut , et chaque terme de cette somme vaut 1 exactement quand est à la fois voisin de et voisin de , c'est-à-dire quand il y a un chemin . La somme compte donc ces chemins.
Le nombre de triangles vaut donc . C'est un résultat qui se démontre en deux lignes, et qui compte les triangles d'un réseau sans jamais les chercher.
1.Un réseau social d'un milliard de comptes, 200 relations chacun. Pour afficher rapidement les amis d'un compte, quelle représentation ?
2.Avec une matrice d'adjacence, parcourir les voisins d'un sommet coûte…
Orienté, pondéré : les mêmes structures, adaptées
Pour un graphe orienté, on ne remplit qu'un sens : la matrice cesse d'être symétrique, et la liste d'adjacence ne contient que les successeurs. Si l'algorithme a besoin des prédécesseurs, il faut construire aussi le graphe inverse.
Pour un graphe pondéré, la case de la matrice contient le poids au lieu de 1, et la liste d'adjacence associe à chaque voisin son poids : elle devient un dictionnaire de dictionnaires, {sommet: {voisin: poids}}. Il faut alors décider ce que vaut une case sans arc : 0 convient tant qu'aucun poids n'est nul, mais un trajet gratuit s'y confondrait avec une absence de trajet ; les calculs de plus court chemin y mettent plutôt l'infini.
Et voici sa matrice. Elle se lit comme la précédente, à une différence près qui change tout.
| A | B | C | D | d⁺ | |
|---|---|---|---|---|---|
| A | 0 | 4 | 2 | 0 | 2 |
| B | 0 | 0 | 0 | 5 | 1 |
| C | 0 | 1 | 0 | 8 | 2 |
| D | 0 | 0 | 0 | 0 | 0 |
| d⁻ | 0 | 2 | 1 | 2 |
Ce que l'orientation fait à la matrice.
La symétrie disparaît. La case vaut 2 et la case vaut 0 : c'est exactement ce que veut dire « on ne peut pas revenir ». Sur un graphe non orienté, la moitié de la matrice était redondante ; ici, chaque moitié porte une information différente.
Il y a désormais deux degrés. Les cases non nulles d'une ligne comptent les arcs qui partent, c'est le degré sortant ; celles d'une colonne comptent ceux qui arrivent, c'est le degré entrant . La figure affiche les deux, en marge droite et en bas. Attention à ne pas additionner les poids : un degré compte des arcs, il ne les pèse pas.
Chercher les prédécesseurs coûte cher, mais pas dans la matrice. Dans la matrice, les successeurs d'un sommet se lisent sur sa ligne, ses prédécesseurs dans sa colonne : cases dans les deux cas. C'est la liste d'adjacence qui est asymétrique : elle range les successeurs de chaque sommet, et trouver ses prédécesseurs oblige à parcourir toutes les listes, en . C'est pour cela qu'un algorithme qui a besoin des prédécesseurs, comme le tri topologique du chapitre 6, qui compte les arcs entrants de chaque sommet, les calcule une fois pour toutes au départ, ou construit le graphe inverse, plutôt que de payer ce parcours à chaque fois.
Les conventions de ce parcours. Un graphe sans poids : un dictionnaire qui associe à chaque sommet la liste de ses voisins, {sommet: [voisins]}, ou de ses successeurs s'il est orienté, sans jamais y inscrire deux fois la même arête.
Un graphe pondéré : un dictionnaire de dictionnaires, {sommet: {voisin: poids}}.
Le dictionnaire qui retient, pour chaque sommet, celui par lequel on l'a atteint : parent.
{u, v} demande deux écritures dans la liste d'adjacence. En oublier une donne un graphe à demi orienté, et les parcours donnent alors des résultats incompréhensibles.Inscrire deux fois la même arête. Une liste Python accepte les doublons : lire deux fois la même ligne d'un fichier ajoute l'arête une seconde fois, sans erreur, et fausse ensuite tous les degrés. Quand les données peuvent en contenir, des ensembles
set de voisins les absorbent, et rendent en prime le test « v est-il voisin de u ? » immédiat, en en moyenne au lieu de .
Sur un graphe non orienté sans boucle, la matrice d'adjacence est…
Mettre en pratique
Quelle représentation pour un réseau routier de 200 000 carrefours ?
Liste d'adjacence.
Un carrefour a rarement plus de six routes, donc est de l'ordre de : le graphe est très creux. La liste occupe une dizaine de mégaoctets.
La matrice demanderait cases, soit des dizaines de gigaoctets pour stocker presque exclusivement des zéros. Et chaque recherche de voisins lirait 200 000 cases pour en trouver au plus six.
Comment vérifier sur la matrice qu'un graphe est non orienté et sans boucle ?
Non orienté : la matrice doit être symétrique, c'est-à-dire pour toute paire.
Sans boucle : la diagonale doit être nulle, car signifierait une arête d'un sommet vers lui-même.
Ces deux contrôles se codent en deux boucles et servent de garde-fou après une lecture de fichier : une matrice non symétrique alors qu'on attendait un graphe non orienté signale presque toujours une arête ajoutée dans un seul sens.
Que vaut le coefficient de , et pourquoi ?
Il vaut le degré du sommet .
Le coefficient de compte les chemins de longueur 2 allant de à , c'est-à-dire les allers-retours . Il y en a exactement un par voisin .
Donc la diagonale de donne la suite des degrés, ce qui fournit au passage un contrôle de cohérence gratuit sur une matrice construite à la main.
Pourquoi une liste d'arêtes reste-t-elle utile malgré ses coûts ?
Parce que certains algorithmes ne demandent jamais « qui sont les voisins de », mais parcourent simplement toutes les arêtes.
C'est le cas de l'algorithme de Kruskal (chapitre 4), qui trie les arêtes par poids croissant et les examine une par une : la liste d'arêtes est exactement la forme dont il a besoin.
C'est aussi le format d'échange le plus courant dans les fichiers, une ligne par arête, parce qu'il est compact et se lit sans connaître le nombre de sommets à l'avance. Le revers : un sommet sans arête, un poste débranché ou une station fermée, disparaît du fichier, sauf à le lister à part.
Les voisins sont rangés dans des listes Python. Quand faut-il leur préférer des ensembles ?
Parcourir les voisins coûte la même chose dans les deux cas, : c'est ce que font presque tous les algorithmes de ce parcours, et la liste, qui garde l'ordre, y suffit.
Le test d'appartenance v in adjacence[u] coûte en revanche sur une liste, contre en moyenne sur un set. Sur un algorithme qui fait ce test dans une boucle interne, cela transforme un en quelque chose de nettement plus lent, sans que rien ne le signale. Le cas le plus courant n'est d'ailleurs pas la liste des voisins, mais l'ensemble des sommets déjà vus d'un parcours : rangé dans une liste, chacun de ses tests coûte , et le parcours du chapitre 3 passe de à .
Une liste autorise en outre les doublons : ajouter deux fois la même arête passe inaperçu et fausse ensuite tous les degrés. Un set les écarte d'office.
Chapitre révisé en octobre 2026 · Une erreur dans ce chapitre ?
Dans la bibliothèque d'exercices
Degrés, matrice d'adjacence et liste d'adjacence.
- Le degré entrant d'un sommetNiveau 1
- De la matrice à la liste d'adjacenceNiveau 2
- Débogage : une arête à sens uniqueNiveau 2
- Matrice ou liste pour 150 000 carrefoursNiveau 2
- Lire A² et A³ sur un réseau de dépôtsNiveau 3
- Les cas d'emploi d'une pièceNiveau 2