edubun
Français

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.

Chargement

Fais glisser pour tourner. Fais défiler ou pince pour zoomer.

Un tableau de cinq valeurs dans un bloc de cinq cases. Déplace le curseur pour choisir i, puis lis, insère ou retire. Un push sur le bloc plein recopie tout dans un bloc deux fois plus grand.

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 laboCroît comme
Lire ou écrire a[i]1Θ(1)
Insérer au débutn déplacements + 1 écritureΘ(n)
Retirer le début1 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
ContexteLes tableaux sont plus anciens que la plupart des langages de programmation. Les premières machines stockaient une table sous forme de mots numérotés en mémoire, et les programmeurs calculaient à la main l'adresse de l'entrée i, exactement comme le fait la formule ci-dessus. Fortran, sorti chez IBM en 1957, a intégré les tableaux au langage : on pouvait écrire A(I) et laisser le compilateur faire le calcul d'adresse. Fortran comptait à partir de 1. C et JavaScript comptent à partir de 0, ce qui va avec la formule : a[0] se trouve à l'adresse de départ elle-même.
InterfaceLa liste des opérations qu'on peut demander, comme get at(i) ou insert last(x).
Structure de donnéesLa façon dont ces opérations sont effectuées en mémoire.
TableauDes valeurs dans un seul bloc de cases égales ; a[i] est à début + i × taille d'une case.
Tableau dynamiqueUn tableau qui grandit en se recopiant dans un bloc plus grand, en général deux fois plus grand.
Coût amortiLe coût moyen par opération sur une longue série, même si certaines opérations isolées sont lentes.
  1. 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
  2. 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) ?
  3. Même tableau de 5 valeurs : combien d'étapes demande le retrait de la première valeur (1 lecture plus les déplacements) ?
  4. Insérer à la position 2 dans un tableau de 5 valeurs (positions 0 à 4) qui a de la place : combien d'étapes ?
  5. 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 ?
  6. Après ce push, quelle est la capacité ?
  7. En partant d'une capacité de 1 et en doublant, combien de copies font 17 push au total ?
  8. 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
  9. 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
  10. 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
L'essentiel
  • 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