Hinweise zur Übung 3 (PID)
2D-Suchbaum
- Sie können sich einen Teil der für diese Übung notwendigen Dateien von der Übungsseite herunterladen (updated 11.4.).
- Zu den einzelnen Methoden:
- insert - Funktioniert sehr ähnlich zu der in der Vorlesung
vorgestellten Variante des Einfügens in einen Binärbaum. Jedoch
muss darauf geachtet werden, dass abwechselnd nach x und nach y-Koordinaten
verglichen werden muss.
- remove - Hier gibt es einen Unterschied Löschen aus
einem Binärbaum. Es gibt keine Möglichkeit in einem 2D-Baum zu
rotieren (das ist notwendig wenn man einen inneren Knoten löschen möchte),
deshalb ist es notwendig sich was anderes einfallen zu lassen (z.B. alle
gelöschten Blätter als "gelöscht" markieren, wobei das in
den anderen Methoden beachtet werden muss). Die einzige Ausnahme ist das
Löschen von Blättern, die kann man nämlich löschen ohne
zu rotieren.
- contains - Hier gilt dasselbe wie für insert.
- findInRangeX / findInRangeY - In der Version der Klasse SimplePointList2D ist es nicht möglich irgendwelche Optimierungen durzuführen. In der Tree2D-Variante
ist es jedoch möglich "Äste" des Baumes "abzuschneiden" (= nicht
mehr durchsuchen). D.h. weiss man, dass sich alle Elemente des gesuchten
Bereichs im linken Ast befinden müssen, braucht man den rechten Ast
nicht durchsuchen, etc.
Falls jemandem diese Methode Probleme macht, so kann man sich dieses "Äste
abschneiden" bei einem "normalen" binären Suchbaum überlegen, für
2D-Bäume funktioniert es nicht anders (nur dass man lediglich bei jedem
zweiten Schritt einen "Ast abschneiden" kann).
Anm.: Diese und die folgenden Methoden machen ohne Optimierungen ("Äste
abschneiden" und so) keinen Sinn, da sie sonst nicht effizienter als die
Methoden in SimplePointList sind.
- findInRectangle - Hier gilt dasselbe wie für findInRangeX/Y, jedoch kann hier das "Äste abschneiden" sowohl in x als auch in y-Richtung vorgenommen werden. Hat man findInRangeX/Y schon fertig geschrieben, so ist diese Methode von der Idee her nicht viel anders.
- findNearest - Der kniffligste Teil.
Ein paar Tipps dazu:
- Es handelt sich um ein geometrisches Problem, daher lohnt es sich (in der Lösungsidee) einige Skizzen zu machen
- Im Prinzip lässt sich findNearest ähnlich optimieren wie findInRectangle, d.h. man kann Ideen von dieser Methode (die etwas weniger aufwändig ist) wiederverwenden.
- Die prinzipielle Idee hinter dieser Methode ist das sukzessive
Verkleinern des Radius um den Ausgangspunkt, in dem der "näheste" Punkt
liegen muss. D.h. wenn ich zuerst die Wurzel des Baumes besuche, dann ist
dieser Punkt der (aktuell) am nächsten liegende Punkt. Und ich weiss
daher, dass alle Punkte, die nicht innerhalb des Radius r = Abstand(Ausgangspunkt, Wurzel)
um den Ausgangspunkt liegen, nicht zu den Kandidaten für den am nächsten
liegenden Punkt gehören (da ich ja schon einen näheren gefunden
habe).
Mit diesem Radius ist natürlich nicht gut rechnen, weshalb man sich
dazu das umgebende Rechteck berechnet. Hat man das getan, hat man eine sehr
ähnliche Problemstellung wie bei findInRectangle. Der einzige Unterschied liegt darin, dass sich das umgebende Rechteck beim Durchlauf durch den Baum weiter verkleinern kann.
- Zusätzlich lässt sich die Suche noch (teils erheblich)
optimieren, wenn man zuerst den Ast besucht, wo mit höherer Wahrscheinlichkeit
der näheste Punkt liegt. D.h. liegt z.B. der Ausgangspunkt links vom
aktuellen Knoten, so sollte man zuerst den linken Zweig besuchen. Damit reduziert
man in den meisten Fällen den Radius schneller und erspart sich so oft
den Besuch des rechten Zweiges (der dann automatisch abgeschnitten wird).
- balance - Zuerst sei dazu bemerkt, dass der treeToVine/vineToTree-Algorithmus nicht für 2D-Bäume anwendbar ist. Deshalb ein paar Tipps:
- Man sammelt sich zuerst alle Knoten in einem Array (die Anzahl der Knoten im Baum sollte man dazu mitgezählt haben in insert und remove)
- Danach baut man sich den Baum von der Wurzel aus neu auf, indem man das Array sortiert (siehe java.util.Arrays.sort(Object[] a, int fromIndex, int toIndex, Comparator c)),
man sich das mittlere Element (=Median) aus dem Array nimmt (=> mittleres
= (erstes + letztes)/2, wobei hier jeweils der Index gemeint ist) und dieses
Element als neue Wurzel hernimmt. Selbiges wiederholt man (rekursiv) für
den linken und den rechten Teil des Arrays (=> linker und rechter Teilbaum).
- Der "Comparator" ist deshalb notwendig, weil man die Punkte
einmal nach der x-Koordinate und einmal nach der y-Koordinate sortieren muss
(es muss jeweils unterschiedlich sortiert werden).
- Es
kann passieren, dass es mehrere Punkte mit gleichem x bzw. y-Wert gibt. In
dem Fall muss man darauf achten, dass man den rechtesten bzw. linkesten Knoten
für die Wurzel nimmt, um das Suchbaum-Kriterium (linker Teilbaum <=
Wurzel < rechter Teilbaum, bzw. linker Teilbaum < Wurzel <= rechter
Teilbaum) zu erhalten.
- Die Methode balance sollte wirklich nur als Fleissaufgabe betrachtet
werden, da es erfordert sich etwas tiefer in die Java-Klassenbibliothek hineinzulesen,
bzw. sich mit Java selbst zu befassen.
- Die Methode findNearest
sollte jedoch noch von einem (angehenden) Mechatroniker gelöst werden
können, da das Problem bei dieser Methode hauptsächlich im Bereich
der Geometrie liegt.
- Die Klasse SimplePointList ist als Hilfe gedacht um sich in
die geometrischen Probleme einzuarbeiten (die leergelassenen Methoden sollten
relativ einfach zu implementieren sein).
- Die Methoden der Klasse Tree2D sind von aufsteigendem Schwierigkeitsgrad, d.h. man sollte die Methoden in folgender Reihenfolge implementieren: insert/contains => remove => findInRangeX/Y => findInRectangle => findNearest => balance. Wer es bis findInRectangle schafft, ist schon ganz gut, bewältigt man noch findNearest, so dürfte der Test kaum mehr ein Problem darstellen :-).