Datastructuren Zoekbomen Deel 1 Bomen Doorlopen Eenvoudiger operaties
Dit onderwerp Bomen: terminologie Bomen: datastructuur voor bomen Zoekbomen: hoe zoek ik in bomen Volgende keer: hoe voeg ik knopen toe en haal ik knopen weg uit zoekbomen (efficient)
Gewortelde boom (rooted tree) Knopen van boom: node Wortel (root) Terminologie I Gewortelde boom (rooted tree) Knopen van boom: node Wortel (root) Voorouder (ancestor) Knoop op pad van x (inclusief) naar wortel Afstammeling (descendant) Proper ancestor; proper descendant Voorouder of afstammeling maar niet x zelf Ouder (parent) Kind (child)
Terminologie II Sibling: knopen met dezelfde ouder (broers) Blad (leaf) Interne knoop (internal node) a b c d e Vb van siblings f g
Terminologie III: graad De graad van een knoop is het aantal kinderen NB: Anders dan in grafen!! Vb. hiernaast Knoop is blad, desd als graad 0 2 1
Terminologie IV: hoogte en diepte Diepte (depth): lengte van pad van x naar de wortel Hoogte (height): maximum lengte van pad van knoop naar een blad onder de knoop 1 1 2 2 dieptes 3 3 3 2 1 hoogtes
Terminologie V: Geordende boom Gewortelde boom met voor elke knoop een volgorde van zijn kinderen vastgelegd
Terminologie VI: Deelboom geworteld bij x De deelboom geworteld bij x (subtree rooted at x) x
Een boom is een binaire boom, als Binaire bomen Een boom is een binaire boom, als De boom leeg is (0 knopen heeft), OF Een wortel en een linkerdeelboom en rechter-deelboom heeft Die linker- en rechter-deelboom zijn weer binair Ze mogen dus ook leeg zijn Verschillende binaire bomen!
Twee manieren om binaire bomen te tekenen
Datastructuren voor gewortelde bomen Nu bespreken we: Datastructuur voor binaire bomen Datastructuren voor gewortelde bomen met willekeurige graad We zagen al eerder een andere: heap Heeft speciale eigenschappen die maken dat we ‘m makkelijk in een array kunnen stoppen (Max- of Min-)Heap-eigenschap
Gewortelde bomen root[T] p Elke knoop x heeft drie pointers: Naar de ouder Notatie: p[x], of p.x of parent.x of … Naar de linkerdeelboom (eventueel leeg) left-child Naar de rechterdeelboom (eventueel leeg) right-child Implementatie van leeg (hangt af van programmeertaal en wat je handig vindt): NIL En we hebben een verwijzing naar de wortel van de boom root[T] of wortel.T of root.T of … root[T] p left right
Datastructuur voor bomen met willekeurig aantal kinderen Elke knoop heeft: Pointer voor de wortel Gelinkte lijst per kind (boek: enkelvoudig gelinkt. Soms nuttig om dubbelgelinkte lijst te maken) Pointer naar eerste kind Namen: p (parent, ouder) left-child right-sibling (rechterbroer / rechterzus)
Dynamische verzamelingen: Zoekbomen Zoekboom: Datastructuur voor Dynamische verzamelingen (bijv. Dictionaries) Operaties die we kunnen doen op verzameling elementen: Search (zoek element op key-waarde, en geef opgeslagen extra informatie) Minimum (wat is de kleinste key-waarde) Maximum (…) Predecessor (Voorganger: gegeven x, wat is de grootste y<x die opgeslagen is) Successor (gegeven x, wat is de kleinste y>x die opgeslagen is) Delete (laat een key (met de extra informatie erbij) weg) Insert (voeg een key (en extra informatie) toe)
Verschillende implementaties Als we “Predecessor” en “Successor” niet belangrijk vinden, is een Hash-tabel een heel goede, en heel veel gebruikte datastructuur Vandaag bekijken we de binaire zoekboom Wordt ook veel gebruikt
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 47 65 42 55 23 67 45 49 60 99 12 31
Binaire zoekboom Je kan ook de NIL’s tekenen 47 65 42 55 23 67 49 60 99 12 31
Binairy search-tree property Als x een knoop is in een binaire zoekboom, en y is een knoop in de linkerdeelboom van x, dan is key[y] £ key[x]. Als x een knoop is in een binaire zoekboom, en y is een knoop in de rechterdeelboom van x, dan is key[x] £ key[y]. 10 20 5
Doorlopen van binaire boom Drie manieren om een binaire boom te doorlopen: Inorder Som eerst de linkerboom op (recursief) Dan de wortel Dan de rechterboom (recursief) Preorder Som eerst de wortel op Dan de linkerboom (recursief) Postorder Som eerst de linkerboom (recursief) En tot slot de wortel op
Inorder-Tree-Walk(x) if (x != NIL) then Inorder-Tree-Walk( left(x)) Print (key(x)) Inorder-Tree-Walk( right(x))
Preorder-Tree-Walk(x) if (x != NIL) then Print (key(x)) Preorder-Tree-Walk( left(x)) Preorder-Tree-Walk( right(x))
Postorder-Tree-Walk(x) if (x != NIL) then Postorder-Tree-Walk( left(x)) Postorder-Tree-Walk( right(x)) Print (key(x))
Eigenschappen Inorder-Tree-Walk Somt van een binaire zoekboom de knopen op in niet-dalende volgorde Gebruikt Q(n) tijd op als we n knopen hebben Stel dat we k knopen in de linkerboom hebben Dan n-k-1 in de rechterboom T(n) = T(k) + T(n-k-1) + d voor constante d Geeft: T(n) = (c+d)n+c met c=T(0) (inductie)
Expressies en notatie * * + / 1 2 3 4 5 Gebruikelijke notatie voor expressies: (1+2)*(3*(4/5)) Infix-notatie * * + / 1 2 3 4 5
Expressies en notatie II Infix: (1+2)*(3*(4/5)) Reverse Polish Notation Postfix-notatie: 1 2 + 3 4 5 / * * Geen haakjes nodig Uitgevonden door Poolse wiskundige Lukasiewicz Stack om e.e.a. uit te rekenen * * + / 1 2 3 4 5
Zoeken in binaire boom Als de key in de wortel zit: klaar Anders: kijk of je in de linker- of rechter-deelboom moet zijn en ga in die boom in recursie Tree-Search(x,k) {Zoek naar key k in een boom met wortel x} if (x == NIL or k == key(x)) then return x if ( k < key(x)) then return Tree-Search(left(x),k) else return Tree-Search(right(x),k)
Zoeken in binaire boom Tree-Search(x,k) {Zoek naar key k in een boom met wortel x} if (x == NIL or k == key(x)) then return x if ( k < key(x)) then return Tree-Search(left(x),k) else return Tree-Search(right(x),k) 47 65 42 55 23 67 49 60 99 12 31
Niet-recursieve (uitgerolde) versie 47 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; 65 42 55 23 67 49 60 99 12 31 Doet hetzelfde, maar zonder recursie Meestal efficienter (in constante in O)
Tijd van Tree-Search Zowel recursieve als iteratieve versie gebruiken O(h) tijd, met h de hoogte van de boom Later: manieren om h = O(log n) te krijgen
Zoeken van Minimum Het minimum is altijd de “meest linkse” knoop Sorry, geen grapjes over politiek hier Tree-Minimum(x) {Zoek het minimum in de boom met wortel x} while (left(x) != NIL) do x = left(x); return x;
Tijd: O(h) – h hoogte van de boom Zoeken van Minimum 47 Tree-Minimum(x) {Zoek het minimum in de boom met wortel x} while (left(x) != NIL) do x = left(x); return x; 65 42 55 23 67 49 60 99 12 31 Tijd: O(h) – h hoogte van de boom
Maximum is meest rechtse knoop Tijd: O(h) met h hoogte van de boom Zoeken van Maximum 47 Tree-Maximum(x) {Zoek het minimum in de boom met wortel x} while (right(x) != NIL) do x = right(x); return x; 65 42 55 23 67 49 60 99 12 31 Maximum is meest rechtse knoop Tijd: O(h) met h hoogte van de boom
Opvolger Opvolger van key x: Twee gevallen: Kleinste van alle andere keys die groter is dan x Twee gevallen: De opvolger zit “onder” x De opvolger zit niet onder x
Opvolger Opvolger van key x: Twee gevallen: Kleinste van alle andere keys die groter is dan x Twee gevallen: De opvolger zit “onder” x Als de rechterdeelboom van x niet leeg is! De opvolger zit niet onder x Als de rechterdeelboom van x wel leeg is…
Opvolgers en voorgangers I Opvolger van knoop x: Als de rechter-deelboom van x niet leeg is, dan is de opvolger van x het minimum van die rechter-deelboom 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)) y = parent(x); ???
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;
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; 40 15 65 8 55 17 67 7 10 29 49 60 99 Zoek bijv opvolger 29
Voorgangers Vinden van voorganger (Tree-predecessor) gaat net zo (“gespiegeld”)
Conclusies 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 (of bijna complete) binaire boom
Samenvatting Terminologie Zoekbomen Operaties op zoekbomen Volgende keren: hoe voeg je knopen toe, en laat je knopen weg uit zoekbomen? En hoe zorg je dat een zoekboom lage diepte blijft houden?