Untitled
unknown
java
a year ago
12 kB
17
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