Arrays
De miljoenste waarde van een array lezen kost één stap, maar een nieuwe waarde vooraan dezelfde array zetten verplaatst een miljoen waarden.
Wat een gegevensstructuur is
Een programma met een namenlijst, een wachtrij afdruktaken of een prijzentabel moet die gegevens ergens opslaan en vragen erover beantwoorden: wat is het derde item, staat deze naam in de lijst, welke taak is de volgende? Een gegevensstructuur is een manier om gegevens op te slaan samen met de methoden die zulke vragen beantwoorden. De cursus 6.006 van MIT onderscheidt hier twee ideeën. De interface zegt welke bewerkingen je mag vragen, zoals ‘geef het item op positie i’ of ‘voeg een item achteraan toe’. De gegevensstructuur bepaalt hoe die bewerkingen werkelijk in het geheugen worden uitgevoerd. Achter één interface kunnen verschillende gegevensstructuren zitten, die enorm in snelheid kunnen verschillen.
In deze cursus tel je stappen, zoals bij algoritmen, om twee structuren zonder stopwatch te vergelijken. In dit hoofdstuk is een stap één geheugencel lezen of schrijven. In het lab hieronder zie je de telling.
Eén blok cellen naast elkaar
Een array slaat waarden op in één aaneengesloten geheugenblok, elke waarde in een cel van gelijke grootte, genummerd vanaf 0. Die indeling maakt arrays snel leesbaar. Als het blok op adres s begint en elke cel c bytes inneemt, staat a[i] op adres s + i × c. De computer berekent dit met één vermenigvuldiging en één optelling en springt er direct heen, of i nu 3 of 3 000 000 is. De MIT-notities geven voor ‘get at(i)’ en ‘set at(i, x)’ op een array Θ(1): de kosten groeien niet met de lengte.
Dezelfde indeling maakt veranderingen in het midden traag. Stel dat de array 7, 3, 9, 4, 6 bevat en je 1 vooraan wilt zetten. Vóór 7 is geen opening, dus elke waarde moet eerst één cel naar rechts, vanaf de laatste zodat niets wordt overschreven. Dat zijn 5 verplaatsingen en 1 schrijfactie: 6 stappen. De voorste waarde verwijderen is het spiegelbeeld: lees haar en verplaats de andere 4 waarden één cel naar links. Invoegen op positie i in een array van n waarden kost n − i verplaatsingen plus één schrijfactie; vooraan is dus de slechtste plek en achteraan de beste.
Sleep om te draaien. Scroll of knijp om te zoomen.
Als het blok vol is
Een geheugenblok heeft een vaste grootte. Wat gebeurt er als je een waarde toevoegt terwijl elke cel bezet is? De array moet een nieuw, groter blok aanvragen en elke waarde kopiëren voordat de nieuwe kan worden geschreven. In het lab kost push op het volle blok van 5 waarden 5 kopieën en 1 schrijfactie. Als de array telkens maar één cel zou groeien, zou elke push alles kopiëren en zouden n pushes ongeveer n²/2 kopieën kosten.
De oplossing uit de MIT-notities is in grote stappen groeien, bijvoorbeeld verdubbelen. Vanaf capaciteit 1 worden kopieën gemaakt bij 1, 2, 4, 8 en 16 waarden. Zo kosten 17 pushes in totaal 1 + 2 + 4 + 8 + 16 = 31 kopieën, minder dan 2 per push. Eén push kan nog traag zijn, maar over elke lange reeks pushes zijn de gemiddelde kosten per push constant. Dit heet geamortiseerde Θ(1)-tijd. Een JavaScript-array gedraagt zich van buitenaf zo: MDN beschrijft arrays als aanpasbaar in grootte en push en pop werken aan het einde, de goedkope kant.
| Bewerking op n waarden | Stappen in het lab | Groeit als |
|---|---|---|
| Lees of schrijf a[i] | 1 | Θ(1) |
| Vooraan invoegen | n verplaatsingen + 1 schrijfactie | Θ(n) |
| Vooraan verwijderen | 1 leesactie + (n − 1) verplaatsingen | Θ(n) |
| Push achteraan (ruimte over) | 1 | Θ(1) |
| Push achteraan (blok vol) | n kopieën + 1 schrijfactie | eenmaal Θ(n), geamortiseerd Θ(1) |
const a = [7, 3, 9, 4, 6];
console.log(a[3]); // 4, one step
a.push(8); // cheap: the end
a.unshift(1); // every value moves one cell right
console.log(a.join(" ")); // 1 7 3 9 4 6 8| Interface | De lijst bewerkingen die je mag vragen, zoals get at(i) of insert last(x). |
| Gegevensstructuur | Hoe die bewerkingen in het geheugen worden uitgevoerd. |
| Array | Waarden in één blok gelijke cellen; a[i] staat op start + i × celgrootte. |
| Dynamische array | Een array die groeit door naar een groter blok te kopiëren, meestal tweemaal zo groot. |
| Geamortiseerde kosten | De gemiddelde kosten per bewerking over een lange reeks, ook als afzonderlijke bewerkingen traag zijn. |
- Hoeveel stappen kost a[700000] lezen uit een array van 1 000 000 waarden volgens de telling van dit hoofdstuk?
- 1
- 20
- 700,000
- 1,000,000
- Een array bevat 7, 3, 9, 4, 6 en heeft ruimte over. Hoeveel stappen kost vooraan invoegen (verplaatsingen plus schrijven)?
- Dezelfde array van 5 waarden: hoeveel stappen kost de voorste waarde verwijderen (1 leesactie plus verplaatsingen)?
- Invoegen op positie 2 in een array van 5 waarden (posities 0 tot 4) met ruimte: hoeveel stappen?
- Een verdubbelende array bevat 5 waarden en heeft capaciteit 5. Je pusht nog een waarde. Hoeveel stappen kost die push?
- Wat is de capaciteit na die push?
- Hoeveel kopieën maken 17 pushes in totaal vanaf capaciteit 1 met verdubbeling?
- Waarom heet push op een verdubbelende array geamortiseerd Θ(1)?
- Elke afzonderlijke push kost precies één stap
- Kopieën zijn zeldzaam, dus het totaal over n pushes blijft onder een constante maal n
- De computer kopieert gratis op de achtergrond
- JavaScript kopieert arrays nooit
- Wat is ‘insert last(x) en delete last()’ in de termen van MIT 6.006?
- een gegevensstructuur
- een interface
- een geheugenadres
- een sorteermethode
- Een array heeft lengte n en telt vanaf 0. Wat is de index van de laatste waarde?
- n
- n − 1
- n + 1
- 1
- Een interface somt bewerkingen op; een gegevensstructuur bepaalt hoe ze worden gedaan.
- Een array bewaart waarden in één blok, dus a[i] kost één stap.
- Invoegen of verwijderen nabij het begin verplaatst ongeveer n waarden.
- Een verdubbelende array maakt push geamortiseerd Θ(1): 17 pushes vanaf capaciteit 1 kopiëren 31 waarden.
Bronnen van deze cursus (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).
Geraadpleegd: 2026-10-18