Probleme – Du tableau rectangulaire au tableau de départs
Exercice · niveau 3 (difficile) · informatique (MP2I/MPI), chapitre 13 — Le modèle des graphes
Énoncé
On veut une représentation par listes d'adjacence qui occupe véritablement , sans allocation dynamique.
- Décrire la représentation par tableau de départs : deux tableaux plats.
- Écrire l'accès aux voisins, et le comparer à celui du tableau rectangulaire.
- Construire cette représentation à partir d'une liste d'arcs, en .
- Vérifier, et dire ce qu'elle interdit.
Corrigé
1. La représentation. On concatène toutes les listes d'adjacence dans un unique tableau arrivee de taille , et l'on retient dans depart l'indice où commence la liste de chaque sommet.
int depart[N_MAX + 1]; /* la liste de u occupe arrivee[depart[u] .. depart[u+1]-1] */
int arrivee[A_MAX]; /* les m arcs, groupés par sommet d'origine */
L'invariant tient en trois points :
departest croissant, et ;- les successeurs de sont exactement à ;
- d'où , lisible en .
La case n'est pas un détail : c'est une sentinelle qui évite un cas particulier pour le dernier sommet.
2. L'accès.
/* Parcourir les voisins de u. Complexité : Theta(d+(u)). */
for (int k = depart[u]; k < depart[u+1]; k = k + 1) {
int v = arrivee[k];
/* ... */
}
Mesuré sur le graphe du chapitre : et . On lit les voisins de aux indices et — soit et —, ceux de à l'indice — soit —, et le sommet n'en a aucun, puisque . Vérifié : ce sont exactement les listes que donne le tableau rectangulaire.
| tableau rectangulaire | tableau de départs | |
|---|---|---|
| mémoire | ||
| parcourir les voisins de | ||
| lire | ||
| ajouter un arc | impossible sans tout décaler |
3. La construction en . On la fait en trois passes, sans jamais trier : c'est un tri par comptage déguisé.
/* Construit depart/arrivee à partir de mm arcs donnés par src[] et dst[].
Précondition : 0 <= src[k], dst[k] < n pour tout k ; mm <= A_MAX.
Complexité : Theta(n + mm) en temps, O(n) en mémoire supplémentaire. */
void construire(int n, int mm, const int src[], const int dst[]) {
/* (a) COMPTER : depart[u+1] recevra d+(u). */
for (int u = 0; u <= n; u = u + 1) { depart[u] = 0; }
for (int k = 0; k < mm; k = k + 1) { depart[src[k] + 1] += 1; }
/* (b) SOMMES PARTIELLES : depart[u] devient l'indice de début de u. */
for (int u = 0; u < n; u = u + 1) { depart[u+1] += depart[u]; }
/* (c) PLACER : un curseur par sommet, initialisé à son début. */
int curseur[N_MAX];
for (int u = 0; u < n; u = u + 1) { curseur[u] = depart[u]; }
for (int k = 0; k < mm; k = k + 1) {
arrivee[curseur[src[k]]] = dst[k];
curseur[src[k]] += 1;
}
}
Le décalage d'un cran en (a) — écrire dans et non dans — est l'astuce qui permet à la passe (b) de transformer les comptes en indices de début par une simple somme partielle, sans tableau auxiliaire. C'est le motif du tri par comptage, et il vaut d'être reconnu : compter, cumuler, placer.
Correction : après (b), , qui est bien l'indice où doit commencer la liste de . En (c), le curseur de parcourt exactement les cases qui lui sont réservées, donc aucune écriture ne déborde sur la liste d'un autre. Terminaison : trois boucles bornées. Complexité : pour (a) et (b), pour (c) — soit , ce qu'on voulait.
4. Ce qu'elle interdit, et pourquoi on l'emploie quand même. Elle interdit l'ajout et le retrait d'arcs : insérer un voisin à demanderait de décaler tout arrivee au-delà, en . C'est une représentation figée, à construire une fois et à lire ensuite.
Or c'est précisément le cas d'usage. Un réseau routier, un graphe du web, un réseau social sont lus des milliards de fois entre deux modifications ; on les reconstruit à intervalles, on ne les édite pas en place. En échange, on obtient trois choses que le tableau rectangulaire ne donne pas : la mémoire exacte , sans marge de sécurité à deviner — et donc sans DEG_MAX arbitraire dont un sommet pourrait sortir ; des voisins contigus en mémoire, donc parcourus à pleine vitesse par le processeur ; et aucune allocation dynamique, donc aucune fuite possible.
C'est le format qu'emploient réellement les bibliothèques de calcul sur graphes et de matrices creuses, sous le nom de stockage par lignes comprimées. Le chapitre a raison de recommander le tableau rectangulaire pour l'épreuve écrite ; celui-ci est ce qu'on écrit quand dépasse le million.
Les autres exercices de ce chapitre Le cours du chapitre
Un blocage sur cet exercice ? Le tuteur d'Adloun guide par questions, sans donner la réponse.