Untitled
unknown
java
a year ago
14 kB
6
Indexable
package no.oslomet.cs.algdat;
import java.util.Iterator;
import java.util.Objects;
import java.util.function.Predicate;
public interface Beholder<T> extends Iterable<T> {
boolean leggInn(T t); // Legger inn t i beholderen
boolean inneholder(T t); // Sjekker om beholderen inneholder t
boolean fjern(T t); // Fjerner t fra beholderen
int antall(); // Returnerer antall elementer i beholderen
boolean tom(); // Sjekker om beholderen er tom
void nullstill(); // Tømmer beholderen
Iterator<T> iterator(); // Returnerer en iterator
default boolean fjernHvis(Predicate<? super T> p) { // Betinget fjerning
Objects.requireNonNull(p); // Kaster unntak om p er null
boolean fjernet = false;
for(Iterator<T> i = iterator(); i.hasNext(); ) { // Itererer over elementer
if (p.test(i.next())) { // Sjekker betingelsen
i.remove(); // fjerner
fjernet = true;
}
}
return fjernet;
}
}
/*------------------------------------------*/
package no.oslomet.cs.algdat;
@FunctionalInterface
public interface Oppgave<T> {
void utførOppgave(T t);
}
/*------------------------------------------*/
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.forelder == null) 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) {
fjernNode(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 fjernNode(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.
fjernNode(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) {
Stabel<Node<T>> stabel = new Stabel<>();
// Bruker stabel 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.push(n); // Legg til funnet node på stabel
n = finnNode(verdi, n.høyre);
}
int fjernet = stabel.size();
while (!stabel.isEmpty())
fjernNode(stabel.pop());
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);
fjernNode(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.
}
}
}
// En stabel-wrapper rundt Deque så vi vet at vi bruker det som en stabel og ikke kø.
class Stabel<T> {
Deque<T> stabel = new ArrayDeque<>();
public boolean push(T verdi) {
stabel.addFirst(verdi);
return true;
}
public T pop() {
return stabel.removeFirst();
}
public T peek() {
return stabel.getFirst();
}
public int size() {
return stabel.size();
}
public boolean isEmpty() {
return stabel.isEmpty();
}
}Editor is loading...
Leave a Comment