Cursus P. Simoens, UGent. Sectieverwijzingen (§) volgen de nummering van deze cursusversie; de leerstoffiche 2025-2026 nummert het NP-hoofdstuk als 14 (hier hoofdstuk 13). Let op: ook de slides van het gastcollege zijn examenleerstof en zitten niet in dit document.
| Structuur | Kerninvariant | Zoeken | Toevoegen | Verwijderen | § |
|---|---|---|---|---|---|
| Binaire zoekboom (BST) | links < knoop < rechts | O(h): gem. O(lg n), slechtst O(n) | idem | idem | §2.1, voorkennis |
| Rood-zwarte boom | 5 regels (zie §2.2), hoogte ≤ 2 lg(n+1) | O(lg n) | O(lg n) | O(lg n) | §2 |
| AVL-boom | hoogteverschil deelbomen per knoop ≤ 1 | O(lg n) | O(lg n) | O(lg n) | §2.1 |
| 2-3-boom / 2-3-4-boom | inwendige knoop 2-3 (2-4) kinderen, alle bladeren zelfde diepte | O(lg n) | O(lg n) | O(lg n) | §2.1 |
| Splay tree | herstructureert ook bij zoeken | geamortiseerd O(lg n) per operatie (gegarandeerd gemiddeld over elke reeks operaties; een individuele operatie kan traag zijn) | idem | idem | §2.1 |
| Treap / randomized search tree | random prioriteit bepaalt vorm | verwacht O(lg n), onafh. van volgorde | idem | idem | §2.1 |
| B-tree / B+ tree | knoop minstens halfvol: blad ≥ ceil(L/2) sleutels, inwendig ≥ ceil(M/2) pointers | O(logM n) blokken | idem | idem | voorkennis GS |
| Hashtabel | hashfunctie + botsingsafhandeling | gem. O(1), slechtst O(n) | idem | idem | voorkennis GS |
| Rasterstructuur | gelinkte lijst per rastercel; vuistregel √n x √n raster | zie opm. | - | - | §3.2 |
| Point quadtree | knoop bevat punt, splitst cel in 4 (ongelijke) rechthoeken | zie opm. | zie opm. | herstructurering deelboom | §3.3.1 |
| PR quadtree | splitst cel in 4 gelijke kwadranten, punten enkel in bladeren | zie opm. | zie opm. | zie opm. | §3.3.2 |
| k-d tree | splitst per niveau volgens 1 dimensie; geen rotaties mogelijk | zie opm. | blad + nieuwe splitsing | lazy deletion | §3.4 |
| Heap (max-heap) | complete binaire boom, ouder ≥ kinderen, in tabel | max: O(1) | O(lg n) | wortel: O(lg n) | bijlage B |
Opmerkingen bij meerdimensionale structuren (de O()-uitdrukkingen van hun woordenboekoperaties zijn geen leerstof, onderstaande feiten wel):
| Algoritme | Strategie | Best | Gemiddeld | Slechtst | Geheugen | Stabiel | Ter plaatse | § |
|---|---|---|---|---|---|---|---|---|
| Selection sort | brute force | Θ(n2) | Θ(n2) | Θ(n2) | O(1) | nee | ja | §5.3 |
| Insertion sort | decrease by 1 | O(n) (gesorteerde invoer) | O(n2) | O(n2) | O(1) | ja | ja | §5.3, §7.1 |
| Binair zoeken | decrease by factor | O(1) | O(lg n) | 2(lg n + 1) vergelijkingen = O(lg n) | O(1) iteratief | - | - | §7.2 |
| Quickselect | decrease, variabele grootte | O(n) | O(n) | O(n2) | O(1) | - | ja | §7.3.1 |
| Interpolerend zoeken | decrease, variabele grootte | O(1) | O(lg lg n) | O(n) | O(1) | - | - | §7.3.2 |
| Mergesort | divide and conquer | Θ(n lg n) | Θ(n lg n) | Θ(n lg n) | O(n) hulptabel | ja | nee | §8.2 |
| Quicksort | divide and conquer | O(n lg n) | O(n lg n) | O(n2) | O(1) (examenantwoord 2024: sorteert ter plaatse) | nee | ja | §8.3 |
| Heapsort | transform and conquer | O(n lg n) | O(n lg n) | O(n lg n) | O(1) | nee | ja | §9.1 |
| Counting sort | tijd kopen met geheugen | Θ(n + k) | Θ(n + k) | Θ(n + k) | O(n + k) | ja (essentieel) | nee | §10.2 |
| Bucket sort | tijd kopen met geheugen | Θ(n) | Θ(n) (uniforme verdeling) | O(n2) | O(n + aantal buckets) | afh. van bucketsort | nee | §10.3 |
| Strassen | divide and conquer | Θ(nlg 7) ~ Θ(n2.81) | idem | idem | - | - | - | §8.1 |
DP-voorbeelden (hoofdstuk 11):
| Probleem | Tijd | Geheugen | § |
|---|---|---|---|
| Fibonacci (tabulation) | O(n) | O(1) met 2 variabelen | §11.2 |
| Binomiaalcoëfficiënten | O(n k) | O(k) met 1 rij | §11.3 |
| Muntselectie | O(n) | O(n), reduceerbaar | §11.5 |
| Langste gemeensch. deelsequentie | Θ(n m) | Θ(n m); enkel lengte: Θ(min(n,m)) | §11.6 |
| Optimale binaire zoekboom | O(n3) | O(n2) | §11.7 |
| Langste palindroom-deelsequentie (examen 2024) | O(n2) | O(n2) | - |
| Betrekking | Oplossing | Schoolvoorbeeld |
|---|---|---|
| T(n) = T(n/2) + c | Θ(lg n) | binair zoeken |
| T(n) = T(n/2) + cn | Θ(n) | - |
| T(n) = 2T(n/2) + c | Θ(n) | (let op: NIET lg n, twee oproepen!) |
| T(n) = 2T(n/2) + cn | Θ(n lg n) | mergesort |
| T(n) = T(n-1) + c | Θ(n) | lineaire recursie |
| T(n) = T(n-1) + cn | Θ(n2) | quicksort slechtste geval |
| T(n) = 2T(n-1) + c | Θ(2n) | examen 2021, vraag 1B |
| T(n) = 7T(n/2) + cn2 | Θ(nlg 7) | Strassen |
| T(n) = aT(n/b) + f(n) | master theorema | zie §6.2.3 |
| # | Bewijs / afleiding | § | Techniek | Status |
|---|---|---|---|---|
| 1 | Min. aantal inwendige knopen RZ-boom: 2z - 1 | §2.3 | inductie op hoogte h | genoteerd |
| 2 | Hoogte RZ-boom is O(lg n): h ≤ 2 lg(n+1) | §2.3 | volgt uit #1 + z ≥ h/2 | genoteerd |
| 3 | Uitwendige weglengte: volle binaire boom met b bladeren heeft uitwendige weglengte minstens b lg b | §10.1 | inductie op hoogte; minimum via afgeleide (b1 = b2 = b/2) | genoteerd |
| 4 | Ondergrens vergelijkingssorteren Ω(n lg n), slechtste geval | §10.1 | beslissingsboom, n! bladeren, hoogtebewijs (volle binaire boom met hoogte h heeft hoogstens 2h bladeren, inductie ook voor h = 0), Stirling | te noteren (examen 2021) |
| 4b | Ondergrens, gemiddelde geval | §10.1 | volgt uit #3: gemiddelde weglengte = uitwendige weglengte / n! ≥ lg(n!) = Ω(n lg n) | volgt uit #3, koppeling noteren |
| 5 | Analyse binair zoeken tot gesloten vorm | §7.2 | recurrentie + backward substitution | te noteren (examen 2024) |
| 6 | Analyses selection en insertion sort | §5.3 | somformules, gevallenonderscheid | te noteren |
| 7 | Analyse mergesort | §8.2 | recurrentie + master/substitutie | te noteren |
| 8 | Quicksort best en slechtst (gemiddeld §8.3.5: niet te kennen) | §8.3.4 | recurrentie | te noteren |
| 9 | Quickselect best en slechtst (gemiddeld: enkel betrekking) | §7.3.1 | recurrentie | te noteren |
| 10 | Heapconstructie is O(n) | bijlage B.2 | sommatie over niveaus | te noteren |
| 11 | Correctheid gretige taakplanning (kortste eerst) | §12.2.1 | exchange argument | te noteren |
| 12 | Correctheid activiteitenselectie (vroegste eindtijd) | §12.2.2 | exchange/greedy stays ahead | te noteren |
| 13 | Geziene NP-reducties, o.a. SAT naar set cover, 3SAT naar vertex cover | §13.4 + theorieles | reductierecept (zie H13) | te noteren |
De afleiding van de gevallen van het master theorema staat bewust niet in dit register: ze valt onder het stuk "Intuïtie in het mastertheorema" (§6.2.3) en is volgens de leerstoffiche niet te kennen.
Variantentraining per bewijs: noteer claim, techniek, kerninvariant, en waar elke aanname het bewijs binnenkomt. Wie weet waar een aanname gebruikt wordt, kan ze vervangen wanneer het examen een variant vraagt.
Definitie (§2.2), de 5 regels:
Eigenschappen (§2.3): minstens 2z - 1 inwendige knopen; hoogte h ≤ 2 lg(n+1), dus alle woordenboekoperaties O(lg n). Een RZ-boom simuleert een 2-3-4-boom: 3-knoop wordt 2 binaire knopen, 4-knoop wordt 3; originele verbindingen zwart, nieuwe rood.
Figuur 2.1: 2-3-4-boom en equivalente rood-zwarte boom
Rotaties (§2.4): lokale operatie rond een ouder-kindverbinding, O(1) (handvol pointers). Behouden de inorder volgorde en dus de zoekboomeigenschap; kleuren wijzigen niet. Linksrotatie rond x met rechterkind y: linkerdeelboom van y wordt rechterdeelboom van x (ordening klopt: die deelboom ligt tussen x en y).
Figuur 2.2: Rotaties
Toevoegen bottom-up (§2.5), beslisboom bij double red (kind c, ouder p, grootouder g, oom u):
Figuur 2.3: Rode oom: recoloring
Figuur 2.5: Rood kind langs binnen (driehoek): eerst rotatie c boven p
Figuur 2.4: Rood kind langs buiten (lijn): rotatie p boven g plus kleurwissel
Nieuwe knoop wordt steeds rood toegevoegd (zwart zou regel 4 breken op alle wegen).
Verwijderen bottom-up (§2.5), de gevallen bij een dubbel zwart probleem (na het verwijderen van een zwarte knoop telt een weg een zwarte knoop te weinig; we duiden de plek aan als dubbel zwart en werken dat weg), met broer b en neven:
Figuur 2.6: Zwarte broer, zwarte neven: recoloring, probleem schuift omhoog
Figuur 2.7: Zwarte broer, rode neef langs buiten: rotatie plus kleurwissels, klaar
Figuur 2.8: Zwarte broer, rode neef langs binnen: eerst herleiden tot het buitengeval
Figuur 2.9: Rode broer: rotatie herleidt tot een geval met zwarte broer
Vereenvoudigde RZ-bomen (§2.7): een AA-boom is een RZ-boom waarbij enkel een rechterkind rood mag zijn. Minder toegelaten vormen betekent minder gevallen in de herstelcode bij toevoegen en verwijderen, ten koste van iets meer rotaties. Een binary B-tree is hetzelfde idee avant la lettre: een 2-3-boom voorgesteld als binaire boom, met dezelfde beperking maar zonder kleurterminologie.
Geaugmenteerde bomen (§2.8): extra veld per knoop voor bijkomende operaties. Voorbeeld size (aantal inwendige knopen in de deelboom, NIL = 0). Kleurwissels raken size niet, rotaties wel. Bij rechtsrotatie van x met linkerkind y (na de rotatie zelf):
Volledige rood-zwarte boom volgens §2.2 t.e.m. §2.5: bottom-up toevoegen (rode oom: recoloring; zwarte oom: driehoek- en lijnrotaties) en bottom-up verwijderen (dubbel zwart: de vier broer/neef-gevallen van figuren 2.6 t.e.m. 2.9). Knopen die door de laatste operatie geherkleurd of geroteerd werden, lichten op. Oefenroutine: voorspel op papier welk geval optreedt, klik dan pas.
Queries (§3.1): point query (specifiek punt), range search (alle punten in een zoekgebied; point query is er een speciaal geval van), nearest neighbour (dichtste punt bij querypunt q, algemener: k dichtste). Triviale methode: alles overlopen, O(n), enkel zinvol bij kleine n of grote zoekgebieden.
Figuur 3.1: De nearest-neighbour en range search
Rasterstructuur (§3.2): raster van hyperrechthoeken, gelinkte lijst per cel. Range search: vind overlappende cellen, doorloop hun lijsten. Compromis celgrootte versus lijstlengte; aantal cellen best een constante fractie van n, vuistregel √n x √n. Faalt bij geclusterde data (alles in 1 cel: O(n)).
Figuur 3.2: Rastercellen die de zoekrechthoek overlappen; geclusterde data degenereert
Point quadtree (§3.3.1): meerdimensionale uitbreiding van een BST. Elke knoop bevat een punt dat zijn cel in 4 (ongelijke) rechthoeken splitst; kinderen NW, NE, SW, SE. Vorm hangt af van toevoegvolgorde. Statische, evenwichtige bouw via lexicografische sortering en medianen: O(n lg n). Verwijderen is lastig: deelboom heropbouwen.
Figuur 3.3: Point quadtree en de bijhorende opdeling van de zoekruimte
Point quadtree volgens §3.3.1 in een begrensd zoekgebied van 100 x 100. Links de zoekruimte: elk punt splitst zijn cel in vier ongelijke rechthoekige kwadranten (NW, NE, SW, SE). Rechts de bijhorende boom met vier kindposities per knoop. Klik in de ruimte om een punt toe te voegen en zie beide weergaven samen evolueren; de vorm hangt af van de toevoegvolgorde. Verwijderen ontbreekt bewust: dat vereist heropbouw van de deelboom (§3.3.1).
PR quadtree (§3.3.2): splitst cellen steeds in 4 gelijke kwadranten (splitspunt hoeft niet opgeslagen, kan berekend worden op de zoekweg), tot elke cel max 1 punt bevat. Punten enkel in bladknopen. Vorm onafhankelijk van toevoegvolgorde, wel van de puntenverdeling. Vereist begrensd gegevensgebied.
Figuur 3.4: Point-region quadtree en de bijhorende opdeling
Point Region quadtree volgens §3.3.2, in hetzelfde begrensde zoekgebied van 100 x 100. Het contrast met de point quadtree hierboven: cellen splitsen steeds in vier gelijke kwadranten rond het celmidden (dat hoeft dus niet opgeslagen te worden), punten zitten uitsluitend in bladeren (max. 1 per blad), en de vorm hangt enkel af van de puntenverdeling, niet van de toevoegvolgorde. Twee dicht bijeen gelegen punten dwingen herhaald splitsen tot ze in verschillende kwadranten vallen. Test de volgorde-onafhankelijkheid met de husselknop.
k-d tree (§3.4): binaire boom, per knoop splitsing volgens 1 dimensie (klassiek: circulair alternerend). Toevoegen aan een blad creëert een nieuwe splitsing. Geen rotaties mogelijk, dus herbalanceren = reconstrueren; verwijderen via lazy deletion.
Figuur 3.5: Toevoegen van een knoop aan een k-d tree creëert een nieuwe splitsing
Query-recept op een k-d tree (§3.4.1, §3.4.2; examenvraag 2024):
Figuur 3.7: De zoekrechthoek kan geheel, gedeeltelijk of niet overlappen met de cel van een knoop
Figuur 3.8: Orthogonal range search in een k-d tree: bezochte knopen en gesnoeide deelbomen
Nearest neighbour: zelfde skelet; zoekgebied is de cirkel/bol rond q met straal = afstand tot het beste punt tot nu toe; snoei elke cel die verder ligt dan die straal.
Figuur 3.9: Te doorzoeken gebieden bij nearest neighbour
k-d tree volgens §3.4, met circulair alternerende splitsing: knopen op even diepte splitsen verticaal op x, op oneven diepte horizontaal op y. Dit is de structuur van examenvraag 2024. Drie operaties om mee te oefenen: een point query, een orthogonal range query (§3.4.1: sleep een rechthoek in de ruimte) en nearest neighbour (§3.4.2). Bij beide queries toont de boom welke knopen bezocht werden (blauw) en hoeveel er dus gesnoeid zijn; bij NN zie je ook de krimpende zoekcirkel als gevolg van elk beter tussenresultaat. De knop "x oplopend" toont de degeneratie tot O(n) die zonder rotaties niet te herstellen valt.
Dit hoofdstuk levert de bouwstenen; de effectieve stappenplannen voor analyses staan in H5 (iteratief) en H6 (recursief).
Raamwerk (§4.1), de drie bouwstenen van elke analyse:
Asymptotische notatie (§4.2), exacte definities:
Figuur 4.2: Asymptotische bovengrens: f(n) = O(g(n))
Figuur 4.3: Asymptotische ondergrens: f(n) = Ω(g(n))
Figuur 4.4: Boven- en ondergrens: f(n) = Θ(g(n))
Rekenregels: O(f) + O(g) = O(max(f, g)); O(f) . O(g) = O(f . g). Veelterm van graad k is O(nk). Elke polynomiale nε (ε > 0) domineert lg n (gebruikt in examen 2021 vraag 1c: n2 lg n verslaat n2.32 voor grote n).
Groeiordes, klein naar groot: 1, lg lg n, lg n, nε, n, n lg n, n2, n3, 2n, n!.
Andere criteria (§4.3): de asymptotische benadering is niet de enige maatstaf. Verborgen constanten tellen in de praktijk (0.1n2 en 2100 n2 zijn beide O(n2)), de benadering geldt enkel voor voldoend grote n, en daarnaast wegen ook geheugengebruik, eenvoud van implementatie en numerieke stabiliteit mee (denk aan Strassen, §8.1).
Figuur 4.5: Typische ordes van toename op lineaire en log-log schaal
Stappenplan iteratieve analyse (cursuskader bij de start van H5; gebruik de bouwstenen uit §4.1):
Voorbeelden in §5.2: enkele lus, geneste lussen.
Somformules (§5.1): som van 1 over i=1..n is n; over i=l..r is r - l + 1; som van i over 1..n is n(n+1)/2; som van i2 is van orde n3/3 (formularium). Let altijd op start- en stopwaarde.
Selection sort (§5.3): zoek telkens het minimum van de restrij en wissel het naar voren. Aantal sleutelvergelijkingen onafhankelijk van de invoer: Θ(n2) in alle gevallen. Niet stabiel (de wissel kan gelijke sleutels voorbijsteken).
Figuur 5.1: Stappen van selection sort
Selection sort volgens §5.3: zoek in de restrij het minimum (oranje), wissel het naar de eerste vrije positie, en breid zo het gesorteerde prefix (groen) uit. Volg de teller: het aantal sleutelvergelijkingen is n(n-1)/2, exact hetzelfde voor elke invoer, ook een al gesorteerde. Vergelijk dat met insertion sort, die op gesorteerde invoer O(n) haalt. Laad een eigen lijst of genereer er een, en speel af of stap manueel.
Insertion sort (§5.3): schuif elk element naar links tot het op zijn plaats zit; prefix is steeds gesorteerd. Gesorteerde invoer: 1 vergelijking per element, O(n). Slechtst (omgekeerd gesorteerd) en gemiddeld: O(n2). Stabiel, ter plaatse.
Figuur 5.3: Enkele iteraties van insertion sort
Insertion sort volgens §5.3 en §7.1: neem het volgende element (oranje, "in de hand"), schuif alle strikt grotere elementen van het gesorteerde prefix een plaats op naar rechts, en voeg het element in op de vrijgekomen plek. Hier zie je het grote verschil met selection sort hierboven: het aantal vergelijkingen hangt WEL af van de invoer. Laad de oplopende preset en je krijgt exact n-1 vergelijkingen (het O(n) beste geval), laad de aflopende en je zit op n(n-1)/2 (het slechtste geval). Merk ook op dat het groene prefix gesorteerd is maar uit de EERSTE elementen van de invoer bestaat, niet uit de kleinste: dat is de vingerafdruk voor herkenningsvragen.
Optimalisatie (§5.4): de voorstelling van data beïnvloedt de efficiëntie sterk. Cursusvoorbeeld: een uitslagtabel rechtstreeks doorzoeken per rang kost O(n2); de tabel eenmalig inverteren naar een indextabel maakt elke opvraging O(1) en het geheel O(n).
Stappenplan recursieve analyse (zelfde stappen 1 t.e.m. 3 als bij H5; dit is wat examen 2024 vraag 4 bedoelt met "doorloop alle stappen van een algoritmische analyse"):
Recurrentie opstellen (§6.1): tel de recursieve oproepen, hun probleemgrootte, en het niet-recursieve werk per oproep. Vergeet het basisgeval niet. Vereenvoudigingen die je mag maken (en moet vermelden): neem n een macht van b zodat floor en ceiling wegvallen.
Iteratieve methode, backward substitution (§6.2.1): vul de betrekking herhaald in zichzelf in, toon minstens twee expliciete substituties, herken het patroon T(n) = T(n/2k) + ..., kies k zodat het basisgeval bereikt wordt (k = lg n), en werk uit tot gesloten vorm. Het examen quoteert op de tussenstappen: rechtstreeks naar het patroon springen kost punten.
Recursieboom (§6.2.2): teken de boom van recursieve oproepen (figuur 6.2), bepaal het werk per niveau en sommeer over de lg n of logb n niveaus. Dit is het denkmodel achter het master theorema.
Figuur 6.2: Algemene vorm van de recursieboom voor T(n) = aT(n/b) + f(n)
Master theorema (§6.2.3), volledig (het theorema en zijn toepassing zijn leerstof; het stuk "Intuïtie in het mastertheorema" binnen deze sectie, met de afleiding van de gevallen, is dat niet): Voor T(n) = a T(n/b) + f(n) (n ≥ n0), T(n) = d (n < n0), met a ≥ 1, b > 1, d ≥ 0:
Werkwijze op het examen (2021 en 2024 quoteren hierop): controleer eerst de vorm, bereken logb(a) expliciet, kies en benoem het geval, toon de voorwaarde (geef een concrete ε of c), en verifieer bij geval 3 de regulariteitsvoorwaarde met een concrete k < 1 (voorbeeld 2024: a=3, b=5, f(n)=n2 geeft 3(n/5)2 = (3/25)n2 ≤ k n2 met k = 4/25).
Drie varianten (§7): verminderen met een constante, met een constante factor, met een variabele grootte.
Verminderen met een constante (§7.1): insertion sort als recursief geformuleerd probleem van grootte n-1 plus tussenvoegen.
Verminderen met een constante factor (§7.2): binair zoeken. Recurrentie slechtste geval: T(n) = T(floor(n/2)) + 2, T(0) = 0 (twee sleutelvergelijkingen per stap). Gesloten vorm voor n macht van 2: T(n) = 2(lg n + 1). Zie bewijzenregister #5.
Varianten op binair zoeken (§7.2), beide O(lg n):
Verminderen met variabele grootte (§7.3):
Figuur 7.2: Lomuto-partitionering
Quickselect volgens §7.3.1 zoekt het k-de kleinste element (de k-de orderstatistiek) zonder volledig te sorteren. Het partitioneert rond een pivot met de methode van Lomuto (figuur 7.2): het eerste element wordt pivot, de rij wordt verdeeld in kleiner-dan-pivot links en groter-of-gelijk rechts, en de pivot komt op zijn definitieve positie s. Daarna telt enkel die positie: ligt k links van s, dan recursie naar links; ligt k rechts, dan naar rechts; valt k op s, dan is het element gevonden. Het verschil met quicksort is precies dat: maar EEN kant wordt verder behandeld. Daardoor gemiddeld O(n), slechtst O(n²). Kies k en speel af.
Figuur 7.3: Positieberekening bij interpolerend zoeken
Interpolerend zoeken volgens §7.3.2 gebruikt wel de waarde van de elementen, in tegenstelling tot binair zoeken. In de deeltabel v[l..r] wordt verondersteld dat de waarden lineair toenemen, op de lijn tussen (l, v[l]) en (r, v[r]). De testpositie m is de afgeronde x-coördinaat van het punt op die lijn met y-coördinaat s (de gezochte sleutel), dus m = l + floor((s - v[l]) / (v[r] - v[l]) * (r - l)). De onderste grafiek toont die interpolatielijn (figuur 7.3): bij een gelijkmatige verdeling springt m bijna meteen naar de juiste plaats, vandaar gemiddeld O(lg lg n). Probeer ook de scheve verdeling om het slechtste geval O(n) te zien.
Implementatie van recursie (§7.4, §7.5): recursie kost stackruimte evenredig met de recursiediepte. Staartrecursie (laatste instructie is de recursieve oproep) kan omgezet worden naar een lus: geheugen van O(diepte) naar O(1). Lineaire recursie: code bevat meerdere oproepen maar voert er per uitvoering maar 1 uit. Vermijd recursion at arm's length: test condities in het basisgeval van de oproep zelf, niet vooraf bij de aanroeper (dupliceert code en tests).
Recursief of iteratief kiezen (§7.5): beide zijn even expressief (elke recursie wordt iteratief door de call stack zelf te simuleren; elke lus wordt staartrecursie). Recursie wint op leesbaarheid en elegantie; iteratie wint op geheugen (geen stackframes) en snelheid (geen oproepkost). Vuistregel: bij enkelvoudige recursie verkies je iteratief; bij meervoudige recursie (twee of meer effectieve oproepen, zoals mergesort) heb je meestal geen keuze en blijf je recursief.
Strassen (§8.1): matrixvermenigvuldiging met 7 in plaats van 8 deelproducten: T(n) = 7T(n/2) + Θ(n2) geeft Θ(nlg 7) ~ Θ(n2.81). De formules zelf worden op het examen gegeven. Nadelen: pas sneller voor zeer grote n, moeilijk paralleliseerbaar, numeriek instabiel.
Mergesort (§8.2): splits in twee helften, sorteer recursief, voeg samen (merge). T(n) = 2T(n/2) + cn geeft Θ(n lg n), alle gevallen. Merge is stabiel (bij gelijke sleutels eerst uit de linkse deeltabel), dus mergesort is stabiel, zowel top-down als bottom-up. Niet ter plaatse: hulptabel O(n), plus O(lg n) stack bij de recursieve versie. Verbeteringen: kleine deeltabellen met insertion sort; merge overslaan als de deelrijen al aansluiten.
Figuur 8.2: Mergesort top-down
Figuur 8.4: Mergesort bottom-up
Mergesort volgens §8.2, top-down. Bovenaan zie je de recursieboom: elke knoop is een deelrij [lo..hi] die in twee helften wordt gesplitst tot je bij deelrijen van 1 element komt (per definitie gesorteerd). Daarna worden de helften terug samengevoegd. De onderste weergave toont dat samenvoegen zelf: de twee gesorteerde helften worden naar een hulptabel gekopieerd (de bron), en tabel A (de bestemming) wordt van links naar rechts opnieuw gevuld door telkens het kleinste kopelement te kiezen. Twee zaken om op te merken, allebei examenstof: de hulptabel maakt mergesort niet ter plaatse (Θ(n) extra geheugen), en omdat we bij gelijke sleutels altijd eerst uit de linkerhelft kiezen is mergesort stabiel. Laad de oplopende preset en kijk naar de teller: de hoeveelheid werk blijft Θ(n lg n), ook bij al gesorteerde invoer, want de recursiestructuur hangt enkel af van n en niet van de waarden. Dat is precies het verschil met insertion sort, die op gesorteerde invoer naar O(n) zakt.
Quicksort (§8.3): kies een pivot, partitioneer (kleiner links, groter rechts, pivot op zijn definitieve plaats), sorteer beide deelrijen recursief. Best en gemiddeld O(n lg n), slechtst O(n2) wanneer de partitie steeds 1 en n-1 elementen oplevert (analyse best/slechtst: §8.3.4; de gemiddelde-gevalanalyse §8.3.5 is geen leerstof). Eerste element als pivot: slechtste geval treedt op bij (omgekeerd) gesorteerde invoer. Random pivot of median-of-three: slechtste geval blijft O(n2) maar wordt zeer onwaarschijnlijk. Conclusie van §8.3.6, het enige dat daarvan leerstof is: een random pivot belandt gemiddeld op een kwart tabellengte van het midden, dus verwacht je opdelingen rond n/4 tegenover 3n/4; de recursieboom is daardoor maar een factor ~2,5 dieper dan in het beste geval, en de kans op het echte slechtste geval wordt verwaarloosbaar klein voor grote n. Dit verklaart ook waarom quicksort in de praktijk sneller is dan mergesort: iets meer vergelijkingen, maar veel minder verplaatsingen. Niet stabiel (de partitie steekt gelijke sleutels voorbij). Sorteert ter plaatse: O(1) hulpgeheugen (examenantwoord 2024). Partitioneren kan met de methode van Lomuto (een index loopt door de rij, kleiner-dan-pivot wordt naar voren gewisseld) of van Hoare (§8.3.2: twee indices lopen naar elkaar toe en wisselen foute paren; minder wissels). Verbeteringen: median-of-three (pivot = mediaan van het eerste, middelste en laatste element, meteen ter plaatse gerangschikt), kleine deelrijen met insertion sort.
Quicksort-varianten (kort). Pivotkeuze: eerste element, willekeurig, of median-of-three (mediaan van eerste, middelste en laatste element); een betere pivot maakt het slechtste geval onwaarschijnlijker. Partitioneren: Lomuto (een loper, pivot op vaste eindplaats) of Hoare (twee lopers, minder wissels, §8.3.2). Driewegspartitionering bij veel duplicaten: elementen gelijk aan de pivot komen apart in het midden. Kleine deelrijen afwerken met insertion sort.
Figuur 8.6: Partitioneren met de methode van Hoare
Quicksort volgens §8.3, met een schakelaar tussen de twee partitiemethodes uit de cursus. Lomuto (figuur 7.2): een leeswijzer j loopt door de deelrij, de grens s schuift mee, elk element kleiner dan de pivot wordt naar voren gewisseld, en op het einde wisselt de pivot naar positie s, zijn definitieve plaats. Hoare (figuur 8.6, pseudocode 8.2, spilelement links): twee lopers i en j bewegen van buiten naar binnen tot ze elkaar kruisen; elementen aan de verkeerde kant worden gewisseld, ook elementen gelijk aan de pivot (dat balanceert duplicaten), en de deelrij wordt op de index j gesplitst. Een belangrijk verschil: bij Hoare komt de pivot NIET op een vaste eindplaats, dus de twee deelrijen [lo..j] en [j+1..hi] dekken samen alle n elementen, terwijl Lomuto er telkens één (de pivot) definitief vastzet. Beide gebruiken het eerste element als pivot, sorteren ter plaatse en zijn niet stabiel. Laad de oplopende preset: met het eerste element als pivot wordt de opsplitsing bij beide methodes erg onevenwichtig, de recursieboom ontaardt tot een ketting en je krijgt het slechtste geval O(n²). Wissel daarna van methode en vergelijk het aantal vergelijkingen en wissels.
Idee: transformeer de invoer naar een representatie waarop het probleem makkelijk is.
Heapsort (§9.1): bouw een max-heap van de tabel (O(n), zie bijlage B.2), verwijder dan n keer de wortel en plaats die achteraan in het gesorteerde deel. Totaal O(n lg n), alle gevallen. Ter plaatse, O(1) extra geheugen, niet stabiel. Herkenningspunt: na de constructiefase is de tabel een heap (grootste element vooraan), na k verwijderstappen staan de k grootste elementen gesorteerd achteraan.
Figuur 9.2: Tweede fase van heapsort: wortel vervangen en laten zakken
Ondergrens vergelijkingssorteren (§10.1): elk algoritme dat enkel sleutelvergelijkingen gebruikt is een binaire beslissingsboom; inwendige knopen zijn vergelijkingen, bladeren zijn de mogelijke uitkomsten (permutaties van de invoer), dus minstens n! bladeren. De ondergrens geldt in twee gevallen, beide Ω(n lg n):
Dit heet de informatietheoretische ondergrens: k uitkomsten onderscheiden via binaire testen vereist soms ceil(lg k) vragen. De grens is tight: mergesort haalt in het slechtste geval n lg n sleutelvergelijkingen.
Figuur 10.1: Beslissingsboom van een vergelijkingssorteerder
Counting sort (§10.2): vereist gehele sleutels uit een beperkt interval van lengte k. Tel per waarde de voorgangers (cumulatieve telling) en plaats elk element direct op zijn eindpositie. Θ(n + k) tijd, O(n + k) geheugen. Stabiel implementeren is essentieel (duplicaten). Geen vergelijkingssorteerder, dus de Ω(n lg n) grens geldt niet.
Figuur 10.2: Counting sort: invoer A, frequentietabel C, uitvoer B
Bucket sort (§10.3): verdeel de sleutels over buckets volgens hun waarde, sorteer per bucket, plak aaneen. Gemiddeld Θ(n) bij uniform verdeelde sleutels, slechtst O(n2) (alles in 1 bucket). De afleiding van het gemiddelde geval is geen leerstof, de conclusie wel.
Figuur 10.3: Bucket sort met gelinkte lijsten per deelinterval
Toepasbaar als (§11.4): het probleem een optimale deelstructuur heeft (de optimale oplossing is uit te drukken in optimale oplossingen van deelproblemen) en deelproblemen overlappen.
Vast recept (gebruik dit bij elke creatievraag, zie examens 2021 en 2024):
Memoisation versus tabulation (§11.1): memoisation is top-down (recursie plus cache, berekent enkel benodigde deelproblemen), tabulation is bottom-up (tabel volledig invullen in afhankelijkheidsvolgorde, geen recursie-overhead).
De betrekkingen per cursusvoorbeeld:
Figuur 11.3: Berekening van een binomiaalgetal: vulvolgorde van de tabel
Figuur 11.4: Muntselectie stap voor stap: F[i] en de gemaakte keuzes
Figuur 11.5: Twee zoekbomen voor dezelfde 5 sleutels met verschillende verwachte zoektijd
Backtracking (§12.1): stel oplossingen voor als tuples (v1, ..., vk); breid kandidaat-deeloplossingen stapsgewijs uit (zoekboom); voldoet een deeloplossing niet meer aan de voorwaarden, keer terug (snoeien). Vindt gegarandeerd alle oplossingen. Schoolvoorbeelden: n-queens, TSP.
Figuur 12.1: Zoekboom voor n-queens (n = 4) met gesnoeide deeloplossingen
Snoeitechnieken (ken ze alle vijf, met de namen):
Inhalige (greedy) algoritmen (§12.2): bouw de oplossing op met lokaal beste keuzes die nooit herzien worden. Snel, maar enkel correct als de gretige keuze bewijsbaar veilig is (exchange argument: verwissel in een veronderstelde optimale oplossing twee elementen en toon dat ze niet slechter, dus tegenspraak of evengoed).
Metaheuristieken (§12.3): algemene recepten voor optimalisatieproblemen; vertrekken van volledige kandidaatoplossingen (niet van deeloplossingen) en vereisen een representatie plus een evaluatiefunctie.
Figuur 12.3: Invloed van de temperatuursparameter op de acceptatiekans
Figuur 12.5: Genetisch algoritme: overgang naar een volgende generatie
Reductie (§13.1): probleem A is in polynomiale tijd reduceerbaar naar probleem B als elke instantie van A in polynomiale tijd omgevormd kan worden naar een instantie van B, zodat de oplossing van B vertaald kan worden naar een oplossing van A. Richting onthouden: A reduceren NAAR B betekent dat B minstens zo moeilijk is als A (B oplossen lost A op).
Figuur 13.3: Een deel van de reductieboom vanuit SAT
Figuur 13.4: NP-compleetheid bewijs je via reductie van een gekend NP-compleet probleem naar jouw probleem
Klassen (§13.2, §13.3):
Reductierecept voor een NP-compleetheidsbewijs (verbetersleutel examen 2021, vier verplichte onderdelen):
Redeneerpatronen waar/vals (examen 2024):
De dertien probleemdefinities (§13.4; op naam kunnen neerschrijven):
Figuur 13.5: Set cover
Figuur 13.6: Exact cover: elk element precies een keer gedekt
Figuur 13.8: Integer partition
Figuur 13.9: Knapsack
Figuur 13.12: Hamiltonian circuit
Plus: Travelling Salesman: gegeven steden en onderlinge afstanden, gevraagd de kortste rondrit die elke stad eenmaal bezoekt.
Omgaan met NP-complete problemen (§13.5): exacte methodes voor kleine instanties (backtracking met snoeien), pseudopolynomiale DP waar mogelijk, benaderingsalgoritmen met garantie, heuristieken en metaheuristieken, en speciale gevallen die wel in P zitten (bv. tweeledige of planaire grafen bij kleuring, 2-elementsverzamelingen bij set cover).
Binaire boom (bijlage A): complete binaire boom in een tabel (niveau per niveau): kinderen van index i staan op 2i en 2i+1, ouder op floor(i/2) (1-gebaseerd). Hoogte van een complete boom met n knopen: floor(lg n).
Heap (bijlage B): complete binaire boom met heapvoorwaarde (max-heap: ouder ≥ kinderen), opgeslagen in een tabel.
Figuur B.1: Stijgende heap: nieuw element schuift langs de weg naar de wortel
Gegeven een tussentijdse tabeltoestand, herken het algoritme aan zijn vingerafdruk:
| Algoritme | Vingerafdruk na k stappen |
|---|---|
| Selection sort | eerste k posities bevatten de k kleinste elementen, definitief gesorteerd; rest ongewijzigd in originele volgorde |
| Insertion sort | eerste k posities gesorteerd, maar het zijn de eerste k ELEMENTEN van de invoer (niet de kleinste); rest exact gelijk aan de invoer |
| Mergesort top-down | linkerhelft volledig gesorteerd verschijnt pas laat; vlak voor de finale merge: twee gesorteerde helften |
| Mergesort bottom-up | opeenvolgende blokken van lengte 2, 4, 8, ... elk intern gesorteerd |
| Heapsort | na constructie: tabel voldoet aan de heapvoorwaarde (maximum vooraan); in de sorteerfase: k grootste elementen gesorteerd achteraan, vooraan een heap |
| Quicksort (pivot op definitieve plaats) | na de eerste partitie: pivot staat exact op zijn eindpositie, alles links kleiner, alles rechts groter (binnen die delen nog ongesorteerd) |
Checklist bij zo'n vraag: (1) vergelijk met de invoerkolom en de gesorteerde kolom, (2) zoek gesorteerde prefixen en bepaal of het de kleinste elementen zijn (selection) of de originele eerste elementen (insertion), (3) zoek gesorteerde blokken van machten van 2 (bottom-up mergesort), (4) test de heapvoorwaarde op de eerste posities, (5) zoek een element waarvoor alles links kleiner en alles rechts groter is (quicksortpivot).