Binære søketrær En ordnet datastruktur med raske oppslag Sigmund Hansen <sigmunha@ifi.uio.no>
Lister og trær Rekke (array): 1 2 3 4 Lenket liste (dobbelt-lenket): 1 2 3 4 Binært søketre: 3 1 4 2 Binære søketrær Puslegruppa INF 2
Lister og trær Lister har data ordnet fra start til slutt Trær har data ordnet fra rot til løv Trær er rekursive datastrukturer hver gren (node) er i seg selv et tre med grener eller barn Et løv er en gren som ikke har noen barn Filsystemet er et typisk tre: Mapper = grener Filer = løv Noder i binære trær har to barn/grener Binære søketrær Puslegruppa INF 3
Binære trær Består av en rotnode Noder har venstre- og høyregrener Rot Venstregren Høyregren Løvnoder har ikke grener 17 I binære søketrær er elementer til venstre mindre enn og elementer til høyre større enn elementet i noden Treet til høyre er et binært søketre 2 8 Grå streker viser nullpekere. 6 Løvnode Binære søketrær Puslegruppa INF 4
Innsetting Hvis det ikke finnes en rotnode, opprett en rotnode med elementet 1 Sammenlikn elementet med gjeldende nodes 2 Hvis det er mindre: Og det ikke finnes et venstrebarn, plasser det til venstre Ellers gå til venstre og fortsett fra 1 3 Hvis det er større eller likt: Og det ikke finnes et høyrebarn, plasser det til høyre Ellers gå til høyre og fortsett fra 1 Binære søketrær Puslegruppa INF
Innsetting Sett inn Ingen rot, opprett noden med Binære søketrær Puslegruppa INF 6
Innsetting Sett inn er mindre enn Ingen venstregren, opprett noden med < Binære søketrær Puslegruppa INF 7
Innsetting Sett inn 17 17 er større enn Ingen høyregren, opprett noden med 17 17 > 17 Binære søketrær Puslegruppa INF 8
Innsetting Sett inn 8 8 er mindre enn Gå til venstre 8 < 8 er større enn Ingen høyregren, opprett noden med 8 8 > 8 17 Binære søketrær Puslegruppa INF 9
Innsetting Sett inn 2 2 er mindre enn Gå til venstre 2 < 2 er mindre enn Ingen venstregren, opprett noden med 2 2 < 2 8 17 Binære søketrær Puslegruppa INF
Innsetting Sett inn 6 6 er mindre enn Gå til venstre 6 < 6 er større enn Gå til høyre 6 > 17 6 er mindre enn 8 2 6 < 8 Ingen venstregren, opprett noden med 6 6 8 Binære søketrær Puslegruppa INF 11
Søking Hvis man skal finne et element, gjøres det på samme måte som innsetting: 1 Hvis gjeldende node er null, finnes ikke elementet. 2 Ellers sammenlikn elementet med gjeldende nodes 3 Hvis det er mindre, gå til venstre og fortsett fra 1 4 Hvis det er større, gå til høyre og fortsett fra 1 Hvis det er likt, har du funnet elementet Binære søketrær Puslegruppa INF 12
Søking Vi skal finne 6 i treet 6 er mindre enn, så gå til venstre 6 er mer enn, så gå til høyre 6 er mindre enn 8, så gå til venstre 6 < 6 er likt 6, så elementet er funnet 6 2 6 < 8 6 > 8 17 Binære søketrær Puslegruppa INF 13
Søking Vi skal finne 16 i treet 16 er større enn, så gå til høyre 16 > 16 er mindre enn 17, så gå til venstre 16 < 17 17 Venstrepekeren er null, så elementet finnes ikke 2 8? 6 Binære søketrær Puslegruppa INF 14
Traversering Tre måter å traversere et tre på: Infiks traversering En node behandles mellom sine venstre- og høyrebarn Prefiks traversering En node behandles før sine barn Postfiks traversering En node behandles etter sine barn Iteratorerer er normalt infiks Noen operasjoner gjøres heller pre- eller postfiks Binære søketrær Puslegruppa INF 1
Traversering Infiks void printinfix(node n) { if (n == null) { return; } } printinfix(n.left); System.out.println(n.element); printinfix(n.right); Binære søketrær Puslegruppa INF 16
Traversering Prefiks void printprefix(node n) { if (n == null) { return; } } System.out.println(n.element); printprefix(n.left); printprefix(n.right); Binære søketrær Puslegruppa INF 17
Traversering Postfiks void printpostfix(node n) { if (n == null) { return; } } printpostfix(n.left); printpostfix(n.right); System.out.println(n.element); Binære søketrær Puslegruppa INF 18
Iterator En iterator må kunne bevege seg tilbake mot roten Kan gjøres med foreldrepekere; dette er litt vanskeligere å programmere, men gir en iterativ løsning Man kan bruke en midlertidig datastruktur til å holde på dataene i treet typisk en stack Vi bruker her den sistnevnte løsningen Binære søketrær Puslegruppa INF 19
Iterator Hva skal legges på den midlertidige strukturen? Hele treet i en kø? Da traverserer man treet og legger inn elementene En mindre del av treet på en stack? Dette gir mindre minnebruk og initiell CPU-bruk Da traverserer man treet kun mot venstre og legger alle disse nodene på stacken Når en node taes av stacken, gjør man den samme operasjonen med høyregrenen til noden Binære søketrær Puslegruppa INF 20
Iterator Traverser hele treet infiks og legg det i en kø Oransje piler viser hvor man går før et element legges i køen Blå piler viser hvor man går etter elementet er lagt i køen Nå kan man bare hente elementer fra køen i next 2 6 8 17 2 6 8 17 Binære søketrær Puslegruppa INF 21
Iterator Begynn med roten og legge alle venstregrener på en stack Oransje piler viser traversering mot venstre som legger noder på stacken 2 2 8 17 6 Binære søketrær Puslegruppa INF 22
Iterator Vi henter ut 2 fra stacken i next Noden har ingen høyrebarn Vi returnerer 2 17 2 8 6 Binære søketrær Puslegruppa INF 23
Iterator Vi henter ut fra stacken i next Vi traverserer høyregrenen mot venstre og legger noder på stacken 8 6 17 Vi returnerer 2 8 6 Binære søketrær Puslegruppa INF 24
Iterator Vi henter ut 6 fra stacken i next Noden har ingen høyrebarn Vi returnerer 6 8 17 2 8 6 Binære søketrær Puslegruppa INF 2
Iterator Vi henter ut 8 fra stacken i next Noden har ingen høyrebarn Vi returnerer 8 17 2 8 6 Binære søketrær Puslegruppa INF 26
Iterator Vi henter ut fra stacken i next Vi traverserer høyregrenen mot venstre Vi returnerer Neste kall på next returnerer det siste elementet 17 2 6 8 17 Binære søketrær Puslegruppa INF 27
Iterator private void stackleftbranch(node n) { if (n == null) { return; } } stack.push(n); stackleftbranch(n.left); Binære søketrær Puslegruppa INF 28
Iterator public E next() { if (!hasnext()) { throw new NoSuchElementException(); } } Node n = stack.pop(); stackleftbranch(n.right); return n.element; Binære søketrær Puslegruppa INF 29
Fjerning Når noe fjernes, flyttes barn oppover i treet Dette gjøres enklest med rekursjon Vi leter etter elementet som i søk, men bytter ut høyre- eller venstrebarnet med det som returneres av rekursive kall Når vi finner elementet, skjer en av følgende: 1 Noden er et løv, returner null 2 Noden har kun ett barn, returner barnet 3 Noden har to barn, fjern det minste barnet i høyregrenen og bytt ut elementet med dennes Binære søketrær Puslegruppa INF 30
Fjerning Søk etter 6 De oransje pekerne endres underveis Vi finner 6, som er et løv 6 < Returnerer null 6 > 17 I noden til 8 settes venstregrenen til returverdien, altså null 2 6 < 8 Løv 6 8 Binære søketrær Puslegruppa INF 31
Fjerning Søk etter 8 Vi finner 8, som har ett barn Returnerer barnet 8 < I noden til settes høyregrenen til returverdien, altså noden som inneholder 6 6 8 > 17 2 8 Ett barn Binære søketrær Puslegruppa INF 32
Fjerning Vi finner, som har to barn Bytt elementet med minste element til høyre < Fjern 6, som er det nye elementet To barn 17 Returner noden selv 2 8 I noden til settes venstregrenen til returverdien, altså den samme noden som før Binære søketrær Puslegruppa INF 33 6
Fjerning public Node remove(node n, E e) { if (n == null) { return null; } int comp = e.compareto(n.element); if (comp < 0) { n.left = remove(n.left, e); } else if (comp > 0) { n.right = remove(n.right, e); Binære søketrær Puslegruppa INF 34
Fjerning } } else if (n.left!= null && n.right!= null) { n.element = getmin(n.right); n.right = remove(n.right, n.element); } else if (n.left!= null) { return n.left; } else { return n.right; } return n; Binære søketrær Puslegruppa INF 3
Balansering Et binært søketre er bare så raskt som det er dypt Det finnes diverse algoritmer for å balansere et tre Disse utføres ved innsetting og fjerning Hvis det blir tid, går vi gjennom en slik algoritme Dere finner uansett en presentasjon på temaet her: http://heim.ifi.uio.no/inf/v12/pusle/avl.pdf Binære søketrær Puslegruppa INF 36