Een datastructuur is een manier om gegevens in het geheugen te ordenen zodat bepaalde bewerkingen snel zijn: een item vinden, er een toevoegen, er een verwijderen.
Geen enkele structuur is overal snel in. Een structuur kiezen betekent kiezen welke bewerkingen goedkoop moeten zijn. De kosten ervan schrijf je in grote O, waarbij n het aantal items is.
De twee basisbouwstenen zijn de array en de gelinkte lijst.
Een array slaat zijn items in één blok geheugen op, na elkaar. De computer kan uitrekenen waar item i staat, dus het lezen van a[i] kost O(1). Vooraan invoegen betekent elk item opschuiven: O(n).
Een gelinkte lijst slaat elk item op in een knoop (node) met een pointer naar de volgende knoop. De laatste pointer is null. Invoegen aan de kop (h) verandert maar één pointer: O(1). Maar om item i te bereiken moet je i pointers volgen: O(n).
Een dubbel gelinkte lijst heeft ook een pointer naar de vorige knoop, zodat je in beide richtingen kunt lopen.
Zelftoets Welke bewerking is O(1) voor een array maar O(n) voor een enkelvoudig gelinkte lijst?
De items een voor een tellen
Een nieuw item vooraan invoegen
Elk item eenmaal bezoeken
Het item op index i lezen
Antwoord: Het item op index i lezen. Een array berekent het adres van item i rechtstreeks. Een gelinkte lijst moet i pointers vanaf de kop volgen. Vooraan invoegen is het omgekeerde geval: goedkoop voor een lijst, duur voor een array.
2Stacks en queues
Een stack (stapel) is als een stapel borden: je voegt alleen bovenaan toe en haalt alleen bovenaan weg. Het laatste item dat erin gaat, komt er als eerste uit: LIFO.
push legt een item bovenop, pop haalt het bovenste item weg, peek leest het bovenste zonder het weg te halen. Alle drie zijn O(1).
Toepassingen: de ongedaanmaken-functie van een editor, de aanroepstapel van een programma, controleren of haakjes in balans zijn, depth-first search.
Een queue (wachtrij) is als een rij bij de kassa: je sluit achteraan aan en vertrekt vooraan. Het eerste item dat erin gaat, komt er als eerste uit: FIFO.
enqueue voegt achteraan toe, dequeue haalt vooraan weg. Beide zijn O(1).
Toepassingen: printopdrachten, berichten die wachten om verwerkt te worden, breadth-first search.
Een deque (double-ended queue) laat toevoegen en weghalen aan beide uiteinden toe. Een prioriteitswachtrij haalt altijd eerst het item met de hoogste prioriteit weg; ze wordt meestal gebouwd als een heap.
Zelftoets Welke datastructuur past het best bij de knop “ongedaan maken” van een teksteditor?
Een graaf
Een hashtabel
Een queue
Een stack
Antwoord: Een stack. Ongedaan maken moet de meest recente actie eerst terugdraaien: last in, first out. Een queue zou de oudste actie eerst ongedaan maken.
3Bomen, heaps, hashtabellen en grafen
Een boom heeft één bovenste knoop, de wortel (root). Elke andere knoop heeft precies één ouder. Knopen zonder kinderen zijn bladeren. Een boom met n knopen heeft n − 1 takken.
In een binaire boom heeft elke knoop hooguit twee kinderen. Een binaire zoekboom (BST) voegt een regel toe: sleutels in de linkerdeelboom zijn kleiner dan de knoop, sleutels in de rechterdeelboom zijn groter.
Zoeken in een BST volgt één pad naar beneden, dus het kost O(hoogte). Een gebalanceerde boom (AVL, rood-zwart) houdt de hoogte dicht bij log₂ n. Sleutels die in gesorteerde volgorde worden ingevoegd, maken een keten met hoogte n.
Een heap is een binaire boom waarin elke ouder kleiner is dan zijn kinderen (min-heap) of groter (max-heap). Het minimum of maximum staat in de wortel, en invoegen en verwijderen kosten O(log n).
Een hashtabel slaat sleutel–waardeparen op. Een hashfunctie zet de sleutel om in een index, zodat invoegen en opzoeken gemiddeld O(1) kosten. Twee sleutels met dezelfde index vormen een botsing (collision), die je oplost met chaining (een lijst per plek) of open adressering (een andere plek proberen).
Een graaf heeft hoekpunten die met bogen (edges) verbonden zijn, die een richting of een gewicht kunnen hebben. Hij wordt opgeslagen als een aangrenzendheidsmatrix, O(V²) geheugen, of als aangrenzendheidslijsten, O(V + E).
Zelftoets De sleutels 50, 30, 70, 20 en 40 worden in deze volgorde in een lege binaire zoekboom ingevoegd. Wat is het linkerkind van 30?
20
40
70
Het heeft geen linkerkind
Antwoord: 20. 20 is kleiner dan 50, dus gaat naar links naar 30. Het is ook kleiner dan 30, dus gaat weer naar links. 40 is groter dan 30 en wordt het rechterkind.
4De methode stap voor stap
Vragen beschrijven vaak een taak en vragen welke structuur past, of vragen hoe een structuur eruitziet na enkele bewerkingen.
Zoek de bewerking die snel moet zijn: toegang op positie, op sleutel, het nieuwste item, het oudste item, het kleinste item, of een bereik.
Koppel die aan een structuur: index → array, sleutel → hashtabel, nieuwste → stack, oudste → queue, kleinste of grootste → heap, gesorteerde volgorde of bereiken → gebalanceerde zoekboom.
Controleer de kosten van de andere bewerkingen die de taak nodig heeft, in grote O.
Voer bij een traceervraag de bewerkingen een voor een uit en noteer de toestand na elke stap.
Controleer de eindtoestand aan de regel van de structuur: LIFO, FIFO, de BST-volgorde of de heap-volgorde.
5Een uitgewerkt voorbeeld
Volg elke stap. Probeer de volgende stap te voorspellen voordat je hem opent.
Uitgewerkt voorbeeld
De sleutels 50, 30, 70, 20, 40 en 60 worden in deze volgorde in een lege binaire zoekboom ingevoegd. Wat is de in-order doorloop, en hoeveel vergelijkingen heeft een zoektocht naar 60 nodig?
1
Voeg de wortel in
De eerste sleutel, 50, wordt de wortel.
2
Voeg 30 en 70 in
30 is kleiner dan 50, dus gaat naar links. 70 is groter dan 50, dus gaat naar rechts.
3
Voeg 20, 40 en 60 in
20: kleiner dan 50, kleiner dan 30, dus links van 30. 40: kleiner dan 50, groter dan 30, dus rechts van 30. 60: groter dan 50, kleiner dan 70, dus links van 70.
4
Doorloop in-order
Een in-order doorloop bezoekt de linkerdeelboom, dan de knoop, dan de rechterdeelboom.
Dat geeft 20, 30, 40, 50, 60, 70: de sleutels in gesorteerde volgorde, zoals bij een BST altijd.
5
Zoek naar 60
Vergelijk met 50: groter, ga naar rechts. Vergelijk met 70: kleiner, ga naar links. Vergelijk met 60: gevonden.
De zoektocht heeft 3 vergelijkingen nodig, een per niveau.
Antwoord
In-order: 20, 30, 40, 50, 60, 70. De zoektocht naar 60 heeft 3 vergelijkingen nodig.
6Veelgemaakte fouten
LIFO en FIFO door elkaar halen. Een stack geeft het nieuwste item terug, een queue het oudste.
Denken dat een gelinkte lijst snelle toegang via index geeft. Je moet vanaf de kop lopen.
Aannemen dat een BST altijd O(log n) is. Alleen een gebalanceerde boom is dat; gesorteerd invoegen geeft O(n).
Botsingen in een hashtabel vergeten. O(1) is het gemiddelde, niet het slechtste geval.
De doorlopen door elkaar halen. Preorder: knoop eerst. In-order: knoop in het midden. Postorder: knoop laatst.
Een aangrenzendheidsmatrix gebruiken voor een grote ijle graaf. Dat verspilt O(V²) geheugen.
Zelftoets Je zet 1, 2 en 3 in deze volgorde in een lege queue. Welk item geeft de eerste dequeue terug?
1, 2 en 3 tegelijk
1
2
3
Antwoord: 1. Een queue is first in, first out, dus 1 vertrekt als eerste. 3 zou het antwoord zijn bij een stack (pop), die het nieuwste item teruggeeft.
7Hoe tentamenvragen eruitzien
Makkelijk: kernbegrippen en feiten. LIFO en FIFO, wat een knoop, wortel of blad is, de BST-regel, wat een hashfunctie doet.
Gemiddeld: concepten toepassen. Welke structuur bij een taak past, het resultaat van push en pop of van BST-invoegingen, boomdoorlopen, een hashtabel met lineair zoeken, kosten in grote O.
Moeilijk: gevallen en analyse. Gebalanceerde bomen, heaps in arrays, B-bomen, tries, union-find, Bloom-filters en ontwerpen zoals een LRU-cache.
Bij een heap die vanaf index 0 in een array is opgeslagen, staan de kinderen van index i op 2i + 1 en 2i + 2, en staat de ouder op ⌊(i − 1) / 2⌋.
Wat is het belangrijkste verschil tussen een statische en een dynamische array?
AEen statische array geeft tragere toegang via de index
BEen dynamische array kan groeien door naar een groter geheugenblok te verhuizen
CEen dynamische array slaat zijn elementen op in gekoppelde knopen zodat hij element voor element kan groeien
DEen statische array kan alleen getallen opslaan
Hints tonen
Python-lists en Java-ArrayLists zijn voorbeelden van een van beide.
Twee foute antwoorden zijn nu doorgestreept. Een van de twee overgebleven antwoorden is goed.
Toon het antwoord en de uitleg
Waarom dit het antwoord is
Een statische array heeft een vaste grootte; een dynamische array reserveert een grotere array en kopieert de elementen als hij vol is.
Antwoord
B · Een dynamische array kan groeien door naar een groter geheugenblok te verhuizen
Voorbeeld 2Gemiddeld
Hoe werkt extract-min in een binaire min-heap?
AVerwijder het laatste element van de array
BVerplaats het laatste element naar de wortel en laat het omlaag zakken (sift down)
CVerwijder de wortel en schuif alle elementen één plaats naar links
DZoek in alle bladeren naar het nieuwe minimum
Hints tonen
Houd de boom compleet en herstel dan de ordening.
Twee foute antwoorden zijn nu doorgestreept. Een van de twee overgebleven antwoorden is goed.
Toon het antwoord en de uitleg
Waarom dit het antwoord is
Het laatste element vult het gat in de wortel en wordt verwisseld met zijn kleinste kind tot de heap-eigenschap geldt, wat O(log n) kost.
Antwoord
B · Verplaats het laatste element naar de wortel en laat het omlaag zakken (sift down)
Voorbeeld 3Moeilijk
Wat zijn, zonder union by rank of path compression, de kosten in het slechtste geval van één find in een union-findstructuur met n elementen?
AO(1)
BO(α(n))
CO(log n)
DO(n)
Hints tonen
De bomen kunnen ontaarden.
Twee foute antwoorden zijn nu doorgestreept. Een van de twee overgebleven antwoorden is goed.
Toon het antwoord en de uitleg
Waarom dit het antwoord is
Naïeve unions kunnen bomen tot een lange keten koppelen, zodat find n − 1 ouderpointers kan moeten volgen; de twee heuristieken brengen het bijna op constante tijd.
Antwoord
D · O(n)
Vragen en antwoorden
Datastructuren: wat is het?
Een datastructuur is een manier om gegevens in het geheugen te ordenen zodat bepaalde bewerkingen snel zijn: een item vinden, er een toevoegen, er een verwijderen.
Geen enkele structuur is overal snel in. Een structuur kiezen betekent kiezen welke bewerkingen goedkoop moeten zijn. De kosten ervan schrijf je in grote O, waarbij n het aantal items is.
Datastructuren: hoe leer je het stap voor stap?
Wat een datastructuur is
Stacks en queues
Bomen, heaps, hashtabellen en grafen
De methode stap voor stap
Een uitgewerkt voorbeeld
Veelgemaakte fouten
Hoe tentamenvragen eruitzien
Datastructuren: wat oefen je?
Makkelijk: kernbegrippen en feiten
Gemiddeld: begrippen toepassen
Moeilijk: casussen en analyse
Elke vraag heeft vier antwoordopties en maar één is juist. Kom je vast te zitten? Open eerst een kleine hint en de volledige uitleg pas als je die nodig hebt.
Zo werkt het oefenen
Elke vraag heeft vier antwoordopties en maar één is juist.
Kom je vast te zitten? Open eerst een kleine hint en de volledige uitleg pas als je die nodig hebt.
Je voortgang, fouten en bewaarde opgaven blijven bewaard, zodat je vóór het tentamen kunt herhalen.