Les tableaux
Lire la millionième valeur d'un tableau prend une étape, mais placer une nouvelle valeur au début de ce même tableau déplace un million de valeurs.
Ce qu'est une structure de données
Un programme qui garde une liste de noms, une file de travaux d'impression ou un tableau de prix doit stocker ces données quelque part, et il doit répondre à des questions à leur sujet : quel est le troisième élément, ce nom est-il dans la liste, quel travail passe ensuite ? Une structure de données est une façon de stocker des données avec les méthodes qui répondent à ces questions. Le cours 6.006 du MIT sépare ici deux idées. L'interface indique quelles opérations on peut demander, comme « obtenir l'élément à la position i » ou « ajouter un élément à la fin ». La structure de données est la façon dont ces opérations sont réellement effectuées en mémoire. Une même interface peut cacher plusieurs structures de données, dont la rapidité peut être très différente.
Ce cours compte les étapes, comme le cours d'algorithmique, pour pouvoir comparer deux structures sans chronomètre. Dans cette unité, une étape est la lecture ou l'écriture d'une case de mémoire. Le labo ci-dessous les compte pour toi.
Un bloc de cases, côte à côte
Un tableau stocke ses valeurs dans un seul bloc de mémoire continu, chaque valeur dans une case de même taille, numérotée à partir de 0. C'est cette disposition qui rend la lecture rapide. Si le bloc commence à l'adresse s et que chaque case occupe c octets, alors a[i] se trouve à l'adresse s + i × c. L'ordinateur calcule cela avec une multiplication et une addition et y va directement, que i vaille 3 ou 3 000 000. Les notes du MIT indiquent que « get at(i) » et « set at(i, x) » sont en Θ(1) pour un tableau, ce qui signifie que le coût n'augmente pas avec la longueur.
Cette même disposition rend lentes les modifications au milieu. Supposons que le tableau contienne 7, 3, 9, 4, 6 et que tu veuilles mettre 1 au début. Il n'y a pas de place devant 7 : chaque valeur doit d'abord se décaler d'une case vers la droite, en commençant par la dernière pour ne rien écraser. Cela fait 5 déplacements et 1 écriture : 6 étapes. Retirer la première valeur est l'opération inverse : on la lit, puis on décale les 4 autres valeurs d'une case vers la gauche. Insérer à la position i dans un tableau de n valeurs coûte n − i déplacements plus une écriture : le début est donc le pire endroit et la fin le meilleur.
Fais glisser pour tourner. Fais défiler ou pince pour zoomer.
Quand le bloc est plein
Un bloc de mémoire a une taille fixe : que se passe-t-il quand on ajoute une valeur alors que toutes les cases sont prises ? Le tableau doit demander un nouveau bloc plus grand et y recopier chaque valeur avant de pouvoir écrire la nouvelle. Dans le labo, un push sur le bloc plein de 5 coûte 5 copies et 1 écriture. Si le tableau ne grandissait que d'une case à chaque fois, chaque push recopierait tout, et n push coûteraient environ n²/2 copies.
La solution décrite dans les notes du MIT consiste à grandir beaucoup d'un coup, par exemple en doublant. En partant d'une capacité de 1, les copies ont lieu quand le tableau contient 1, 2, 4, 8 et 16 valeurs : 17 push coûtent donc 1 + 2 + 4 + 8 + 16 = 31 copies au total, moins de 2 par push. Un push isolé peut encore être lent, mais toute longue série de push revient en moyenne à un coût constant par push. On parle de temps Θ(1) amorti. Vu de l'extérieur, un tableau JavaScript se comporte ainsi : MDN décrit les tableaux comme redimensionnables, et push et pop agissent à la fin, qui est le côté bon marché.
| Opération sur n valeurs | Étapes dans le labo | Croît comme |
|---|---|---|
| Lire ou écrire a[i] | 1 | Θ(1) |
| Insérer au début | n déplacements + 1 écriture | Θ(n) |
| Retirer le début | 1 lecture + (n − 1) déplacements | Θ(n) |
| Push à la fin (place libre) | 1 | Θ(1) |
| Push à la fin (bloc plein) | n copies + 1 écriture | Θ(n) une fois, Θ(1) amorti |
const a = [7, 3, 9, 4, 6];
console.log(a[3]); // 4, une étape
a.push(8); // bon marché : la fin
a.unshift(1); // chaque valeur se décale d'une case vers la droite
console.log(a.join(" ")); // 1 7 3 9 4 6 8| Interface | La liste des opérations qu'on peut demander, comme get at(i) ou insert last(x). |
| Structure de données | La façon dont ces opérations sont effectuées en mémoire. |
| Tableau | Des valeurs dans un seul bloc de cases égales ; a[i] est à début + i × taille d'une case. |
| Tableau dynamique | Un tableau qui grandit en se recopiant dans un bloc plus grand, en général deux fois plus grand. |
| Coût amorti | Le coût moyen par opération sur une longue série, même si certaines opérations isolées sont lentes. |
- Avec la façon de compter de cette unité, combien d'étapes demande la lecture de a[700000] dans un tableau de 1 000 000 valeurs ?
- 1
- 20
- 700,000
- 1,000,000
- Un tableau contient 7, 3, 9, 4, 6 et a de la place libre. Combien d'étapes demande l'insertion d'une valeur au début (déplacements plus écriture) ?
- Même tableau de 5 valeurs : combien d'étapes demande le retrait de la première valeur (1 lecture plus les déplacements) ?
- Insérer à la position 2 dans un tableau de 5 valeurs (positions 0 à 4) qui a de la place : combien d'étapes ?
- Un tableau qui double contient 5 valeurs et sa capacité est de 5. Tu fais un push de plus. Combien d'étapes prend ce push ?
- Après ce push, quelle est la capacité ?
- En partant d'une capacité de 1 et en doublant, combien de copies font 17 push au total ?
- Pourquoi dit-on que push sur un tableau qui double est en Θ(1) amorti ?
- Chaque push prend exactement une étape
- Les copies sont rares, donc le total sur n push reste inférieur à une constante fois n
- L'ordinateur recopie gratuitement en arrière-plan
- JavaScript ne recopie jamais les tableaux
- Dans le vocabulaire du MIT 6.006, « insert last(x) et delete last() » est
- une structure de données
- une interface
- une adresse mémoire
- une méthode de tri
- Un tableau a une longueur n et compte à partir de 0. Quel est l'indice de sa dernière valeur ?
- n
- n − 1
- n + 1
- 1
- Une interface liste des opérations ; une structure de données est la façon de les effectuer.
- Un tableau garde ses valeurs dans un seul bloc : a[i] prend donc une étape.
- Insérer ou retirer près du début déplace environ n valeurs.
- Un tableau qui double rend push Θ(1) amorti : 17 push depuis une capacité de 1 recopient 31 valeurs.
Sources de ce cours (11)
- MIT OpenCourseWare 6.006 Introduction to Algorithms, Spring 2020 (Demaine, Ku, Solomon)
- lecture notes 2 Data Structures (interface vs data structure, Sequence and Set interfaces, stack = insert_last/delete_last, queue = insert_last/delete_first, array, linked list, dynamic array, amortized Θ(1) append)
- 4 Hashing (chaining, pigeonhole collisions, chain length n/m)
- 6 Binary Trees Part 1 (height, traversal order, O(h) operations) and 7 Binary Trees Part 2: AVL (Adelson-Velsky and Landis, 1962) at ocw.mit.edu/courses/6-006-introduction-to-algorithms-spring-2020/pages/lecture-notes
- MDN Web Docs, Array.prototype.shift (shifts all values left by 1)
- Array.prototype.unshift, Map (insertion order, Map vs Object, spec requires sublinear average access) and Set (developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects)
- ECMAScript 2025 Language Specification (tc39.es/ecma262)
- 24.1 Map Objects and 24.2 Set Objects ("must be implemented using either hash tables or other mechanisms that, on average, provide access times that are sublinear on the number of elements")
- Array.prototype.shift steps (Repeat, while k < len: move element k to k - 1)
- D. E. Knuth, The Art of Computer Programming vol. 3 (2nd ed. 1998) section 6.4 History (H. P. Luhn, IBM internal memorandum January 1953, hashing with chaining; digit pairs added mod 10, 31415926 -> 4548) via gwern.net copy, and IEEE Spectrum, "Hans Peter Luhn and the Birth of the Hashing Algorithm" (2018)
- "A brief history of the stack", SIGCIS (sigcis.org/files/A brief history.pdf: Bauer and Samelson applied for the patent in 1957, US 3,047,228 granted July 31, 1962; IPL list processing by Newell, Simon and Shaw at RAND 1956) and Wikipedia, Information Processing Language (Newell, Shaw, Simon, RAND and Carnegie Institute of Technology, about 1956).
Consulté le: 2026-10-18