Adloun

Le modèle des graphes

Cours complet · informatique (MP2I/MPI), chapitre 13 · MP2I et MPI

Travailler ce chapitre sur Adloun Exercices corrigés de ce chapitre

13.1 Un modèle, et presque tout devient un graphe

Le programme range les graphes parmi les « structures de données relationnelles », et le mot est juste : là où l'arbre impose une hiérarchie, le graphe ne dit rien de plus que « ces objets sont reliés ». C'est cette pauvreté qui fait sa portée.

Il demande de « mettre en avant des applications importantes et si possible modernes : réseau de transport, graphe du web, réseaux sociaux, bio-informatique », et de « préciser autant que possible la taille typique de tels graphes ». Ces ordres de grandeur ne sont pas anecdotiques — ils décident de l'algorithme qu'on a le droit d'écrire.

GrapheSommetsArcsCe qu'un donnerait
Métro d'une villeinstantané
Réseau routier français opérations : des jours
Graphe du webinconcevable
Réseau social mondialinconcevable
ImportantLa leçon de ce tableau

Sur un graphe de sommets, un algorithme en est inutilisable, et un algorithme en tourne en quelques secondes. C'est pourquoi la représentation choisie — matrice ou listes — n'est pas un détail d'écriture : elle change la complexité de tout ce qu'on bâtira dessus.

13.2 Définitions

Définition 13.1Graphe orienté, graphe non orienté

Un graphe est la donnée d'un ensemble fini de sommets (ou nœuds) et d'un ensemble de liens.

  • Si les liens sont des couples ordonnés, on parle d'arcs et le graphe est orienté.
  • Si ce sont des paires non ordonnées, on parle d'arêtes et le graphe est non orienté.

On note et . Une boucle est un arc ou une arête d'un sommet vers lui-même. Le programme précise : « on n'évoque pas les multi-arcs » — entre deux sommets, il y a au plus un lien dans chaque sens.

Définition 13.2Degrés

Dans un graphe non orienté, le degré est le nombre d'arêtes incidentes à . Dans un graphe orienté, le programme fixe les notations :

Proposition 13.3Lemme des poignées de main

Dans un graphe non orienté, . Dans un graphe orienté, .

Démonstration

Chaque arête est comptée une fois dans et une fois dans : la somme des degrés compte donc chaque arête deux fois. Chaque arc , lui, est compté une seule fois dans et une seule dans .

Définition 13.4Chemins et cycles

Un chemin de à est une suite de sommets où chaque est un arc (ou une arête). Sa longueur est , le nombre d'arcs — et non de sommets.

Un cycle est un chemin de longueur non nulle dont l'origine et l'extrémité coïncident. Dans un graphe non orienté, on exige de plus qu'il n'emprunte pas deux fois la même arête, faute de quoi tout aller-retour serait un cycle.

Définition 13.5Connexité
  • Un graphe non orienté est connexe si tout couple de sommets est relié par un chemin. Ses composantes connexes sont ses parties connexes maximales.
  • Un graphe orienté est fortement connexe si pour tout couple il existe un chemin de à et un de à .
AttentionFortement connexe est bien plus fort que connexe

Le graphe orienté est connexe si l'on oublie les orientations, et n'est pas fortement connexe : on ne revient jamais de vers . La différence est celle d'une ville aux rues à sens unique : deux quartiers peuvent sembler voisins sur la carte et n'être joignables que par un long détour, voire pas du tout. La recherche des composantes fortement connexes fait l'objet du chapitre chap:graphes-avances.

Définition 13.6Trois familles remarquables
  • Un graphe orienté acyclique — un dag — est un graphe orienté sans cycle. Il modélise toute relation de dépendance : tâches à ordonner, cellules d'un tableur, calcul d'un programme. Le lien avec les ordres bien fondés du chapitre chap:induction est exact.
  • Un arbre est un graphe non orienté connexe et acyclique. Une forêt est un graphe acyclique, c'est-à-dire une réunion disjointe d'arbres.
  • Un graphe est biparti si l'on peut partager en deux parties et telles que toute arête relie un sommet de à un sommet de .
iRemarqueDeux définitions de l'arbre, et elles se rejoignent

Le chapitre chap:arbres définissait l'arbre par induction, avec une racine. Ici il est défini comme un graphe connexe acyclique, sans racine privilégiée. Les deux coïncident dès qu'on désigne un sommet comme racine : la structure inductive est alors déterminée. Un arbre au sens des graphes possède exactement arêtes — une de moins que de sommets — et c'est une caractérisation utile.

Proposition 13.7Caractérisation des graphes bipartis

Un graphe est biparti si et seulement s'il ne contient aucun cycle de longueur impaire.

Démonstration (Sens direct)

Soit biparti de parties et . Un chemin alterne nécessairement entre et : après un nombre pair d'arêtes on est revenu du même côté, après un nombre impair on est de l'autre. Un cycle revient à son origine, donc du même côté : sa longueur est paire.

iRemarque

La réciproque se démontre par un parcours qui colorie alternativement — c'est exactement l'algorithme de bicolorabilité que le programme cite au chapitre chap:parcours. La caractérisation est donc effective : elle donne un test en .

Définition 13.8Pondération et étiquettes

On peut attacher une valeur aux arcs : une pondération numérique (distance, coût, capacité) ou une étiquette quelconque. Le programme motive cet ajout « par des exemples concrets : graphe de distance, automate fini, diagramme de décision binaire » — et l'automate fini du chapitre chap:automates est exactement cela : un graphe orienté dont les arcs portent des lettres.

13.3 Représenter un graphe

Tout ce qui précède est mathématique. Le choix de la représentation, lui, est informatique — et il décide de la complexité de tous les algorithmes à venir.

13.3.1 Matrice d'adjacence

Définition 13.9Matrice d'adjacence

est la matrice définie par s'il existe un arc de vers , et sinon. Pour un graphe pondéré, on y range le poids, avec une valeur convenue pour l'absence d'arc.


/* Matrice d'adjacence : un tableau statique à deux dimensions. */
#define N_MAX 100
int m[N_MAX][N_MAX];      /* m[u][v] vaut 1 s'il existe un arc u -> v */

13.3.2 Listes d'adjacence

Définition 13.10Listes d'adjacence

À chaque sommet on associe la liste de ses voisins sortants. La place occupée est alors proportionnelle au nombre d'arcs, et non à son carré.


(* Le graphe ci-dessus, en listes d'adjacence. *)
let g = [| [1; 2]; [3]; []; [2] |]

Méthode : Les listes d'adjacence en C, sans allocation dynamique

Le programme donne une indication précise : « la présentation en C s'effectue à travers des tableaux statiques. Pour la représentation en liste d'adjacence, on peut considérer un tableau à deux dimensions dont les lignes représentent chaque liste avec une sentinelle ou un indicateur de taille en premier indice. »


#define N_MAX 100
#define DEG_MAX 50

/* voisins[u][0] est le NOMBRE de voisins de u ;
   voisins[u][1..voisins[u][0]] sont ces voisins. */
int voisins[N_MAX][DEG_MAX + 1];

/* Ajoute l'arc u -> v. Précondition : voisins[u][0] < DEG_MAX. */
void ajouter_arc(int u, int v) {
    assert(voisins[u][0] < DEG_MAX);
    voisins[u][0] = voisins[u][0] + 1;
    voisins[u][voisins[u][0]] = v;
}

/* Parcourir les voisins de u : */
for (int k = 1; k <= voisins[u][0]; k = k + 1) {
    int v = voisins[u][k];
    /* ... */
}

Aucun malloc, aucun free, aucun pointeur fou : c'est le procédé à retenir pour les épreuves écrites, où l'allocation dynamique est un risque inutile.

13.3.3 Le choix, et il est décisif

ImportantComparer les deux représentations
MatriceListes
Mémoire
Tester « l'arc existe-t-il ? »
Parcourir les voisins de
Parcourir tous les arcs
Ajouter un arc

La ligne décisive est la troisième. Un parcours de graphe visite les voisins de chaque sommet : il coûte avec une matrice et avec des listes. Sur le réseau routier français — sommets, arcs — cela fait contre : des jours contre une seconde.

Définition 13.11Graphe creux, graphe dense

Un graphe est creux quand , dense quand approche . La règle :

  • creux — presque tous les graphes réels — : listes d'adjacence ;
  • dense, ou si l'on teste sans cesse l'existence d'un arc : matrice.

Le réseau routier a un degré moyen de : sa matrice occuperait cases pour arcs, soit un remplissage de . La matrice n'y est pas seulement lente, elle ne tient pas en mémoire.

iRemarqueChaque algorithme aura sa représentation

Le programme le dit pour les deux algorithmes de plus courts chemins : « on présente l'algorithme de Dijkstra … en lien avec la représentation de graphes par listes d'adjacences. On présente l'algorithme de Floyd-Warshall en lien avec la représentation de graphes par matrice d'adjacence. » Ce n'est pas une convention d'exposition : Dijkstra parcourt des voisins, Floyd-Warshall consulte des couples. Chacun est écrit pour la structure qui répond à sa question — chapitre chap:parcours.

Exemple 13.12Une propriété de la matrice qui sert

Si est la matrice d'adjacence d'un graphe non pondéré, le coefficient de compte les chemins de longueur exactement de à .

Démonstration (Par récurrence sur )

Vrai pour par définition. Si c'est vrai pour , alors

et cette somme compte, pour chaque , les chemins de longueur de à prolongés par l'arc lorsqu'il existe. Tout chemin de longueur est obtenu ainsi, une seule fois, en désignant son avant-dernier sommet.

13.4 Ce qu'il faut retenir

ImportantLe modèle, en cinq points
  • , avec et ; degrés et ; .
  • Connexe (non orienté) et fortement connexe (orienté) sont deux notions très différentes.
  • Trois familles : dag pour les dépendances, arbre pour les hiérarchies ( arêtes), biparti pour les appariements — et biparti équivaut à « aucun cycle impair ».
  • Matrice : de mémoire, test d'arc en . Listes : , parcours des voisins en .
  • Les graphes réels sont creux. Sauf raison contraire, on prend les listes — et le choix se fait avant d'écrire l'algorithme, pas après.

Continuer sur Adloun : animation, QCM, fiches, exercices