Untitled
unknown
java
5 months ago
12 kB
14
Indexable
package no.oslomet.cs.algdat; import java.util.*; public class SøkeBinærTre<T> implements Beholder<T> { public static void main(String[] args) { SøkeBinærTre<Integer> tre = new SøkeBinærTre<>(Comparator.naturalOrder()); int[] a = {6, 3, 9, 1, 5, 7, 10, 2, 4, 8, 11, 6, 8}; for (int verdi : a) tre.leggInn(verdi); for (Integer verdi : tre) System.out.println(verdi); } // En del kode er ferdig implementert, hopp til linje 91 for Oppgave 1 private static final class Node<T> { // En indre nodeklasse private T verdi; // Nodens verdi private Node<T> venstre, høyre, forelder; // barn og forelder // Konstruktører private Node(T verdi, Node<T> v, Node<T> h, Node<T> f) { this.verdi = verdi; venstre = v; høyre = h; forelder = f; } private Node(T verdi, Node<T> f) { this(verdi, null, null, f); } @Override public String toString() {return verdi.toString();} } // class Node private final class SBTIterator implements Iterator<T> { Node<T> neste; public SBTIterator() { neste = førstePostorden(rot); } public boolean hasNext() { return (neste != null); } public T next() { Node<T> denne = neste; neste = nestePostorden(denne); return denne.verdi; } } public Iterator<T> iterator() { return new SBTIterator(); } private Node<T> rot; private int antall; private final Comparator<? super T> comp; public SøkeBinærTre(Comparator<? super T> c) { rot = null; antall = 0; comp = c; } public boolean inneholder(T verdi) { if (verdi == null) return false; Node<T> p = rot; while (p != null) { int cmp = comp.compare(verdi, p.verdi); if (cmp < 0) p = p.venstre; else if (cmp > 0) p = p.høyre; else return true; } return false; } public int antall() { return antall; } public String toStringPostOrder() { if (tom()) return "[]"; StringJoiner s = new StringJoiner(", ", "[", "]"); Node<T> p = førstePostorden(rot); while (p != null) { s.add(p.verdi.toString()); p = nestePostorden(p); } return s.toString(); } public boolean tom() { return antall == 0; } // Oppgave 1 public boolean leggInn(T verdi) { // Sjekker om vi prøver legge inn null Objects.requireNonNull(verdi, "Kan ikke sende inn null i treet."); // Om treet er tomt, legg inn ny node som rot if (tom()) { rot = new Node<>(verdi, null); antall++; return true; } Node<T> n = rot; int cmp; while (true) { // Returnerer inni, så burde komme oss ut av løkka cmp = comp.compare(verdi, n.verdi); if (cmp < 0) { // Skal vi til venstre? if (n.venstre == null) { // Om venstreplass er ledig, legg inn her. n.venstre = new Node<>(verdi, n); antall++; return true; } else { n = n.venstre; } } else { // Vi skal til høyre if (n.høyre == null) { // Om høyreplass er ledig, legg inn her. n.høyre = new Node<>(verdi, n); antall++; return true; } else { n = n.høyre; } } } } // Oppgave 2 // Trenger ofte finne en spesifikk node, tar det like gjerne ut i egen logikk. // Tar inn en verdi (å finne) og en node ("rotnoden", lar oss lette i subtrær) // Gir ut første node i subtreet med n som rot som har gitt verdi. Gir null om vi ikke finner verdien. private Node<T> finnNode(T verdi, Node<T> n) { if (verdi == null) return null; while (n != null) { int cmp = comp.compare(verdi, n.verdi); if (cmp < 0) n = n.venstre; else if (cmp > 0) n = n.høyre; else break; } return n; } public int antall(T verdi){ // Om vi har tomt tre vil alle verdier gi 0. Om vi spør om null, vil svaret alltid være 0. if (tom() || verdi == null) return 0; // Har rota verdien vi leter etter? Om ja, begynn telling på 1. int ant = 0; Node<T> n = finnNode(verdi, rot); // Første node med gitt verdi. while (n != null) { ant++; // Dette teller den *forrige* funnede noden n = finnNode(verdi, n.høyre); // Finn neste node med verdien, i det høyre subtreet. /* Dette er litt tregere enn det hadde trengt. Neste node har samme verdi som forrige, så vi vet at vi primært skal mot venstre, og maks 1 steg mot høyre av gangen. Men logikken er lettere å følge om vi bruker finnNode her, i stedet for å gå gjennom stegene selv. */ } return ant; } // Oppgave 3 private Node<T> førstePostorden(Node<T> p) { if (p.venstre != null) { // Om du har et venstrebarn, er du ikke først i postorden, og riktig svar er første i postorden // i ditt venstre subtre return førstePostorden(p.venstre); } else if (p.høyre != null) { // Om du ikke har et venstrebarn, men har et høyrebarn, er du ikke først i postorden, og riktig svar // er første i postorden i ditt høyre subtre return førstePostorden(p.høyre); } else { // Om du verken har venstre eller høyrebarn, så er rota først i postorden i dette subtreet return p; } } private Node<T> nestePostorden(Node<T> p) { // Om du er rota, er det ingen etter deg if (p == rot) return null; // Om du er til din foreldres høyre, eller din forelder ikke har flere barn, er din forelder neste i postorden if (p.forelder.høyre == p || p.forelder.høyre == null) return p.forelder; // Om du er til din foreldres venstre, og din forelder har flere barn, finn første i postorden til din foreldres høyre. return førstePostorden(p.forelder.høyre); } // Oppgave 4 public void postOrden(Oppgave<? super T> oppgave) { /* Både førstePostorden og nestePostorden er i min implementasjon rekursive, poenget med denne oppgaven var bare at *denne* funksjonen ikke kaller på seg selv. Går bra at funksjonene førstePostorden og nestePostorden er rekursive. Denne metoden er jo veldig lik toStringPostorder, så trikset er jo å bare se hvordan den er skrevet. */ if (tom()) return; Node<T> n = førstePostorden(rot); while (n != null) { oppgave.utførOppgave(n.verdi); n = nestePostorden(n); } } public void postOrdenRekursiv(Oppgave<? super T> oppgave) { postOrdenRekursiv(rot, oppgave); // Ferdig implementert } private void postOrdenRekursiv(Node<T> p, Oppgave<? super T> oppgave) { if (p == null) return; postOrdenRekursiv(p.venstre, oppgave); postOrdenRekursiv(p.høyre, oppgave); oppgave.utførOppgave(p.verdi); // Blir postorden siden utførOppgave er sist } // Oppgave 5 public boolean fjern(T verdi) { // Vi finner noden som skal fjernes, og fjerner denne. Om vi ikke finner noden, er ikke verdien der. Node<T> n = finnNode(verdi, rot); if (n != null) { fjern(n); antall--; // Antall må trekkes fra her, og ikke i fjern(Node), siden fjern(Node) kalles rekursivt og ville da trukket fra flere ganger return true; } return false; } private void fjern(Node<T> n) { // Gitt en (ikke-null) node, fjerner denne Node<T> f = n.forelder; /* Option 1: Noden har ingen barn. Vi fjerner denne ved å oppdatere forelderens peker til null. */ if (n.venstre == null && n.høyre == null) { if (f == null) { // Vi er rota, setter rot til null. rot = null; } else if (f.venstre == n) { // Vi er venstrebarnet til forelder f.venstre = null; } else { // Vi er høyrebarnet til forelder f.høyre = null; } n.forelder = null; // Null ut foreldrepeker } /* Option 2: Noden har to barn. Jeg sjekker dette her, for da *vet* jeg at noden må ha nøyaktig ett barn etter dette. Ellers måtte jeg sjekket noe a la (n.venstre != null && n.høyre == null). Vi finner neste node i inorden, flytter dens verdi opp hit, og sletter den noden i stedet. */ else if (n.venstre != null && n.høyre != null) { Node<T> r = n.høyre; while (r.venstre != null) r = r.venstre; // Bytt verdi med neste i inorden n.verdi = r.verdi; // Fjern denne noden. Den vil ha maks ett høyrebarn. fjern(r); } /* Option 3: Noden har kun et venstrebarn. Vi setter forelders peker til å peke på dette venstrebarnet. */ else if (n.venstre != null) { if (f == null) { // Vi er rota. Må oppdatere rotnoden rot = n.venstre; } else if (f.venstre == n) { f.venstre = n.venstre; } else { f.høyre = n.venstre; } n.venstre.forelder = f; n.forelder = null; n.venstre = null; // Null ut pekere } /* Option 4: Noden har kun et høyrebarn. Samme som option 3, essensielt. */ else { // Noden har nøyaktig ett høyrebarn if (f == null) { // Vi er rota. Må oppdatere rotnoden rot = n.høyre; } else if (f.venstre == n) { f.venstre = n.høyre; } else { f.høyre = n.høyre; } n.høyre.forelder = f; n.forelder = null; n.høyre = null; // Null ut pekere } } public int fjernAlle(T verdi) { Deque<Node<T>> stabel = new ArrayDeque<>(); // Bruker kø så vi sletter nederste element først // Ellers måtte vi flyttet hver lik verdi opp til toppen hver gang, helt til siste Node<T> n = finnNode(verdi, rot); while (n != null) { stabel.addFirst(n); // Legg til funnet node på stabel n = finnNode(verdi, n.høyre); } int fjernet = stabel.size(); while (!stabel.isEmpty()) fjern(stabel.removeFirst()); antall = antall - fjernet; return fjernet; } public void nullstill() { if (tom()) return; // Gå gjennom treet i postorden, slett noder når du kommer til dem // Hver node som slettes vil da ha 0 barn. (Dette kun sant om postorden) Node<T> n = førstePostorden(rot); Node<T> fj; while (n != null) { fj = n; // Må ha midlertidig node, gå videre i postorden før vi sletter noden. n = nestePostorden(n); fjern(fj); antall--; // Kunne bare satt antall = 0 til slutt. // Ved å trekke fra hver gang er det derimot lettere å se om noe har gått galt, da får vi feil antall. } } }
Editor is loading...
Leave a Comment