1 Datastructuren Zoekbomen II Invoegen en weglaten
2 Dit onderwerp Binaire Zoekbomen: vorige keer Wat is een binaire zoekboom (eigenschap) Zoeken Minimum Maximum Opvolger / Voorganger Binaire Zoekbomen: nu Een extra query (vb) Invoegen Weglaten Binaire Zoekbomen: hierna Optuigen: snelle “order queries” Balanceren: zorg dat de diepte O(log n) blijft
3 Binaire zoekboom Wortel bevat een key (met pointer naar bijbehorend object met extra gegevens …) Linker deelboom bevat alleen kleinere () keys Rechter deelboom bevat alleen grotere () keys Soms sta je toe dat een key vaker voorkomt, soms niet
4 Binaire zoekboom Je kan ook de NIL’s tekenen
5 Zoeken in een binaire boom Iterative-Tree-Search(x,k) while (x != NIL and k != key(x)) do if ( k < key(x)) then x = left(x) else x = right(x) return x; Kost O(h) tijd als boom hoogte h heeft
6 Zoeken van Minimum Tree-Minimum(x) {Zoek het minimum in de boom met wortel x} while (left(x) != NIL) do x = left(x); return x; Het minimum is altijd de “meest linkse” knoop Tijd: O(h) – h hoogte van de boom Het minimum is altijd de “meest linkse” knoop Tijd: O(h) – h hoogte van de boom
7 Zoeken van Maximum Tree-Maximum(x) {Zoek het minimum in de boom met wortel x} while (right(x) != NIL) do x = right(x); return x; Maximum is meest rechtse knoop Tijd: O(h) met h hoogte van de boom Maximum is meest rechtse knoop Tijd: O(h) met h hoogte van de boom
8 Opvolgers en voorgangers I Opvolger van knoop x: 1. Als de rechter- deelboom van x niet leeg is, dan is de opvolger van x het minimum van die rechter-deelboom 2. Als die wel leeg is dan zit die opvolger NIET onder x … Tree-Successor(x) if (right(x) != NIL) then return Tree- Minimum(right(x)) else … x
9 Opvolger die niet onder x zit Stel x heeft lege rechter-deelboom Ga omhoog de boom in, zolang je in rechterdeelbomen zit Dus je gaat “links- omhoog” Daarna ga je nog 1 keer een stap omhoog Tree-Successor(x) if (right(x) != NIL) then return Tree- Minimum(right(x)) y = parent(x); while (y != NIL and x == right(y)) do x = y; y = parent(y); return y;
10 Tree-successor Tree-Successor(x) if (right(x) != NIL) then return Tree- Minimum(right(x)) y = parent(x); while (y != NIL and x == right(y)) do x = y; y = parent(y); return y;
11 Lineair in de hoogte Alle operaties: Search, Minimum, Maximum, Successor, Predecessor kunnen in O(h) met h de hoogte van de boom Er bestaan zoekbomen met hoogte O(lg n), bijvoorbeeld neem een complete binaire boom Later zien we hoe we logaritmische diepte kunnen handhaven
12 Veel andere queries Er zijn veel andere vragen die snel beantwoord kunnen worden mbv een binaire zoekboom Voorbeeld: Geef in volgorde alle keys die k zijn voor een gegeven k (k hoeft niet zelf in de boom te zitten)
13 Code voor voorbeeld Geef in volgorde alle keys die k zijn voor een gegeven k (k hoeft niet zelf in de boom te zitten) Idee: recursief algoritme: vergelijk k met waarde van wortel en ga dan in recursie in de delen waar dat nodig is … Roep aan: ListKG(root.T, k) ListKG(node x, sleutel k) if (x == NIL) then Doe niets else if key(x) k then ListKG(left(x)); Druk af key(x); ListKG(right(x)) else ListKG(left(x))
14 Analyse voorbeeld Correct, want … In goede volgorde want inorder! Let op rol van NIL’s Tijd: O(r + h) als we r antwoorden rapporteren en de boom hoogte h heeft ListKG(node x, sleutel k) if (x == NIL) then Doe niets else if key(x) k then ListKG(left(x)); Druk af key(x); ListKG(right(x)) else ListKG(left(x))
15 Invoegen en weglaten Invoegen: Nieuwe knopen kunnen we altijd als blad toevoegen Het algoritme zoekt een goede plek op waar de nieuwe key als blad kan worden toegevoegd Weglaten is wat moeilijker (straks)
16 Invoegen Tree-insert voegt een knoop altijd toe in een blad (NIL-knoop) We lopen eerst naar beneden in de boom en zoeken een blad Dat blad zit vlakbij z Tree-Insert(T,z) {z is een knoop met left(z) == right(z) == NIL} y = NIL; x = root(T); while (x != NIL) do y = x; {y is steeds de ouder van x} if (key(z) < key(x)) then x = left(x) else x = right(x) {x is een blad van de boom “vlakbij” key(z)} … en dan …
18 Invoegen - II Als we ‘t blad gevonden hebben hangen we z onder y Als y == NIL: de boom was eerst leeg Anders kijk je of je z links of rechts onder y moet hangen Tree-Insert(T,z) y = NIL; x = root(T); while (x != NIL) do y = x; if (key(z) < key(x)) then x = left(x) else x = right(x) parent(z) = y; if (y == NIL) then root(T) = z (T lege boom) else if (key(z) < key(y) then left(y) = z else right(y) = z
19 Weglaten Weglaten: als je een blad weglaat: makkelijk Anders: je kan de knoop niet zomaar weglaten, dan krijg je een gat Verplaats een andere knoop naar het ontstane gat: welke? Later: herbalanceren ?
20 Weglaten Bekijk verschillende gevallen: Knoop heeft geen kinderen (makkelijk) Knoop heeft alleen linkerkind Knoop heeft alleen rechterkind (net zo als vorige geval, maar gespiegeld) Knoop heeft zowel linkerkind als rechterkind
21 Weglaten: geval 1 1e geval: knoop heeft geen kinderen Dan kan je de knoop gewoon weglaten Ouder van z krijgt een pointer naar NIL erbij Tree-Delete(T,z) {z is een knoop in de boom T} if (left(z)==NIL and right(z) == NIL) then if (parent(z) == NIL) then T = NIL {z was de enige knoop, de boom is nu leeg} elseif (left(parent(z))== z) then left(parent(z)) = NIL else right(parent(z)) = NIL Gooi knoop z weg
22 Weglaten – geval 2a Geval 2a: z heeft maar 1 kind, zeg een linkerkind en een ouder Dan hangen we dat kind van z direct onder de ouder van z … if (left(z) != NIL and right(z) == NIL and parent(z) != NIL) then x = left(z); if (z == left(parent(z)) then left(parent(z)) = x parent(x) = parent(z); Gooi z weg else right(parent(z)) = x; parent(x) = parent(z); Gooi z weg z is niet de wortel
23 Geval 2b z heeft alleen een linkerkind, geen rechterkind, maar ook geen ouder (is de wortel) Dan laten we z weg, en wordt het linkerkind de wortel van T if (left(z) != NIL and right(z) == NIL and parent(z) == NIL) then T = left(z); Gooi z weg z is de wortel
24 Weglaten 3 Als z alleen een rechterkind heeft gaat e.e.a. net zo als geval 2 (2a en 2b), met links en rechts verwisseld if (left(z) == NIL and right(z) != NIL) … 17 29
25 Weglaten 4 Laatste geval: z heeft twee kinderen Veel lastiger! Let op: hier verschillen CLRS 2e druk en CLRS 3e druk (en later) Idee: vind de opvolger van z en verplaats die naar de plek van z
26 Weglaten – een nuttig lemma Lemma. Als z twee kinderen heeft, dan heeft de opvolger y (successor) van z geen linkerkind Bewijsidee. Stel dat x een linkerkind is van y. Dan kan je laten zien dat z < x < y en dus is y niet de opvolger van z, tegenspraak. QED z right(z) y x
27 Weglaten: twee subgevallen Laat y de opvolger zijn van x Geval 4-1: y is het rechterkind van x Geval 4-2: y is niet het rechterkind van x x x y y
28 Weglaten: geval 4-1 (y rechterkind x) We laten x weg y is het de opvolger van x Stel y is het rechterkind van x We verplaatsen de node van y naar de plek van x Goed zetten: Pointer van ouder van x naar x gaat nu naar y Linkerkind van x wordt linkerkind van y Als x de wortel was, wordt y de wortel x y y
29 Weglaten: geval 4-1 (y rechterkind x) y = successor(x); if (parent(y) == x) then: if (parent(x) == NIL) then: {wortel} Root(T)=y if (parent(x) != NIL and left(parent(x)==x) {x is linkerkind} left(parent(x) = y parent if (parent(x) != NIL and right(parent(x)==x) {x is rechterkind} right(parent(x) = y … x y y
30 Weglaten: geval 4-1 (y rechterkind x) … parent(left(x))= y left(y) = left(x) parent(y) = parent(x) {Rechterkind van y blijft hetzelfde} {Nu kan x weggegooid} x y y
31 Weglaten: geval 4-2 (y niet rechterkind x) y wordt verplaatst naar positie van x Rechterboom van y komt op plek van y x y y
32 Stap 1: pointers van en naar de ouder is net als geval 4-1 Oldparenty = parent(y) if (parent(y) == x) then: if (parent(x) == NIL) then: {wortel} Root(T)=y if (parent(x) != NIL and left(parent(x)==x) {x is linkerkind} left(parent(x)) = y parent if (parent(x) != NIL and right(parent(x)==x) {x is rechterkind} right(parent(x)) = y
33 Daarna linker en rechterkinderen {links net als 4-1} left(y) = left(x) parent(left(x))= y {rechterkind van y nu goed zetten:} w = right(y) parent(w) = oldparenty left(parent(w))=w right(y) = right(x) parent(right(y))=y x y y 39 29
34 Opmerking De code in het boek is Compacter Moeilijker te snappen dat het correct werkt Werkt in ongeveer dezelfde tijd
35 Tijd Alle operaties: zoeken, invoegen, weglaten, minimum, maximum opvolger, voorganger, kunnen in O(h) tijd, met h de hoogte van de binaire zoekboom Verschillende andere operaties kunnen in O(n) tijd, zie ook werkcollege: Opsommen van knopen in gesorteerde volgorde … Hoe groot kan h zijn?
36 Hoe groot kan h zijn? Beste geval: plm. lg n (complete binaire boom) Slechtste geval: n (elke knoop heeft maar 1 kind)
37 Gebalanceerde bomen Extra werk bij invoegen en weglaten om te zorgen dat de hoogte van de boom O(log n) blijft Veel verschillende oplossingen hiervoor. Wij bekijken een elegante vorm: de rood-zwart- bomen (red-black trees) en varianten