Beispiel

Werbung
4 Bäume
4.1 Begriffe und Konzepte
Hierarchisches Strukturierungshilfsmittel
Bäume: Vererbungsbäume, Dateisysteme, arithmetischer Ausdruck als Baum
usw.
Fahrzeug
KFZ
LKW
Prof. Dr. Manh Tien Tran
PKW
Fahrrad
Motorrad
1
4 Bäume
Arithmetischer Ausdruck a + b*c - d/e
+
a
*
b
Prof. Dr. Manh Tien Tran
/
d
e
c
2
4 Bäume
Definition
Unter einem orientierten Wurzelbaum versteht man Menge von Knoten und
Kanten mit den Eigenschaften:
●
●
Es gibt genau einen ausgezeichneten Knoten (die Wurzel).
Jeder Knoten, mit Ausnahme der Wurzel, ist durch genau eine Kante mit
seinem Vaterknoten (Elternknoten,Vorgänger, parent) verbunden. Er wird
dann auch Kind (Sohn, Nachfolger, child) dieses Knotens genannt. Zwei
Knoten mit demselben Vater heißen Brüder (Geschwister, siblings).
Ein Knoten ohne Kinder heißt Blatt. Die anderen Knoten bezeichnet man als
innerer Knoten. Jeder Knoten ist die Wurzel eines Unterbaums, welcher aus
ihm und den Knoten unter ihm besteht.
Bemerkung
Ein Wurzelbaum ist ein azyklischer (kreisfreier) Graph (siehe später)
Prof. Dr. Manh Tien Tran
3
4 Bäume
Wurzel
Kante
innerer Knoten
+
a
/
*
b
d
e
c
Blatt
Prof. Dr. Manh Tien Tran
4
4 Bäume
Bäume sind Mengen mit hierarchischer (rekursiver) Struktur.
Alternative Definition
Ein orientierter Wurzelbaum (oder kurz: Wurzelbaum) ist eine endliche Menge B
von Objekten, die entweder leer ist, oder für die folgendes gilt:
●
●
in B existiert ein ausgezeichnetes Element w (die Wurzel von B). Wurzel hat
keinen Vorgänger
Die Elemente B \ { w } können disjunkt zerlegt werden in B1, B2, ... , Bm, wobei
jeder Bi ein orientierter Baum ist.
Prof. Dr. Manh Tien Tran
5
4 Bäume
Anders ausgedrückt:
●
Die leere Menge { } und { w } sind beides Bäume
●
Wenn B1, ... , Bm Bäume sind, dann ist B = { w,B1, B2,...,Bm } ein Baum.
B1, B2,...,Bm heißen Unterbäume von B.
Algorithmen, die auf Bäumen arbeiten, haben oft rekursive Ausprägungen.
Prof. Dr. Manh Tien Tran
6
4 Bäume
Definition
Der äußere Grad (auch positiver Grad, Ausgangsgrad) eines Knotens ist die
Anzahl seiner direkten Nachfolger.
Ein Pfad in einem Baum ist eine Folge von unterschiedlichen Knoten, in der die
aufeinander folgenden Knoten durch Kanten verbunden sind.
Unter dem Niveau (Stufe) eines Knotens versteht man die Länge des Pfads
(Anzahl der Kanten) von der Wurzel zu dem Knoten.
Die Höhe eines Baumes entspricht dem maximalen Niveau eines Blatts plus 1.
Das Gewicht eines Baumes ist die Anzahl seiner Blätter.
Bemerkung
Ein Blatt hat den Grad 0. Ein innerer Knoten hat einen Grad > 0.
Prof. Dr. Manh Tien Tran
7
4 Bäume
Niveau 0
+
a
*
b
Prof. Dr. Manh Tien Tran
Niveau 1
/
d
c
e
Niveau 2
Niveau32
8
4 Bäume
Definition
●
●
●
Ist die maximale Anzahl n der Kinder vorgegeben, so spricht man man von nären Baum.
Sind die Kinder jedes Knotens in einer bestimmten Reihenfolge geordnet,
wird ein solcher Baum als geordneter Baum bezeichnet.
Binärbäume sind geordnete Bäume, bei den jeder Knoten maximal zwei
Kinder hat. Die Unterbäume heißen rechter und linker Unterbaum.
Bemerkung
Die Unterscheidung von rechten und linken Unterbäumen ist wichtig (wegen der
Reihenfolge).
Prof. Dr. Manh Tien Tran
9
4 Bäume
Definition
●
●
●
Zwei Binärbäume heißen ähnlich, wenn sie dieselbe Struktur haben.
Zwei Binärbäume heißen äquivalent, wenn sie ähnlich sind und dieselbe
Information enthalten.
Ein Binärbaum der Höhe h heißt vollständig, wenn jeder innere Knoten
nicht leere linke und rechte Unterbäume hat und jeder Knoten auf dem
Niveau h-1 (letztes Niveau) Blatt ist.
-
●
vollständiger Baum der Höhe 3
+
a
Prof. Dr. Manh Tien Tran
/
*
d
e
10
4 Bäume
Bemerkung
Die maximale Anzahl von Knoten auf dem Niveau k ist 2k
Die Anzahl von Knoten in einem vollständigen Binärbaum der Höhe h ist 2h – 1.
Definition
●
Ein Binärbaum heißt voll, wenn jedes Niveau mit inneren Knoten ausgefüllt
ist, evtl. mit Ausnahme des letzten Niveaus.
Prof. Dr. Manh Tien Tran
11
4 Bäume
4.2 Darstellung von binären Bäumen
Möglichkeiten:
- statische Darstellung mit Hilfe von Arrays
- dynamische Darstellung (mit Hilfe von Referenzen/Verweisen)
Flexibiltät
Speicherbedarf
Zugreifbarkeit
statisch
-
-
+
dynamisch
+
+
-
Im Folgenden betrachten wir die Darstellung mit Referenzen. Für statische
Darstellungen siehe Literatur.
Prof. Dr. Manh Tien Tran
12
4 Bäume
4.2.1 Verkettete Darstellung
public class CTree<E> {
private CTreeNode<E> mRoot;
Ref auf Daten
// ...
}
Referenz auf den linken
Teilbaum
Referenz auf den rechten
Teilbaum
class CTreeNode<E> {
private int mKey;
// wird verwendet für binäre Suchbäume
private E mData;
// Verweis auf Datenobjekt
private CTreeNode<E> mLeft; // Verweis auf den linken Unterbaum
private CTreeNode<E> mRight; // Verweis auf den rechten Unterbaum
...
Prof. Dr. Manh Tien Tran
13
4 Bäume
4.2.1.1 Konstruktoren
public CTreeNode() { this(0,null, null, null); }
public CTreeNode(E data) { this(0, data, null, null); }
public CTreeNode(int key, E data) { this(key, data, null, null); }
public CTreeNode(int key, E data, CTreeNode<E> left, CTreeNode<E> right)
{
mKey = key;
mData = data;
mLeft = left;
mRight = right;
}
Prof. Dr. Manh Tien Tran
14
4 Bäume
4.2.1.2 setter/getter-Methoden
public int getKey() { return mKey; }
public void setKey(int key) { mKey = key; }
public E getData() { return mData;}
public void setData(E data) { mData = data; }
public CTreeNode<E> getLeft() { return mLeft; }
public void setLeft(CTreeNode<E> left) { mLeft = left; }
public CTreeNode<E> getRight() { return mRight; }
public void setRight(CTreeNode<E> right) { mRight = right; }
Prof. Dr. Manh Tien Tran
15
4 Bäume
Man kann mit diesen einfachen Methoden Bäume konstruieren (alle Keys seien
0)
A
CTreeNode<String> l = new CTreeNode<String>("D");
CTreeNode<String> r = new CTreeNode<String>("E");
B
C
r = new CTreeNode<String>(0,"C", l, r);
l = new CTreeNode<String>("B");
D
E
CTreeNode<String> root = new CTreeNode<String>(0,"A", l, r);
Prof. Dr. Manh Tien Tran
16
4 Bäume
4.2.2 Durchlaufen (Traversierung) von verketteten binären Bäumen
Ziel: Systematisches Aufsuchen jedes Knotens und Verarbeitung des
Knoteninhalts.
Möglichkeiten (mit der Konvention "links vor rechts")
●
●
●
●
Inorder-Durchlauf (Zwischenordnung): zuerst wird der linke Teilbaum
besucht, dann der Knoten selbst und anschließend der rechten Teilbaum.
Preorder-Durchlauf (Vorordnung): Knoten, linker dann rechter Teilbaum.
Postorder-Durchlauf (Nachordnung): linker, rechter Teilbaum dann
Knoten.
Levelorder-Durchlauf: Breitensuche, d.h. auf jedem Niveau des Baumes
werden alle Knoten besucht, bevor auf das nächste Niveau gewechselt wird.
Prof. Dr. Manh Tien Tran
17
4 Bäume
Beispiel
Inorder:
A
DBEAHFICG
Preorder:
B
D
C
ABDECFHIG
Postorder:
E
F
H
Prof. Dr. Manh Tien Tran
G
I
DEBHIFGCA
Levelorder:
ABCDEFGHI
18
4 Bäume
Implementierungen
1. Möglichkeit: Mit Hilfe der Rekursion. Zum Beispiel zur Ausgabe in Preorder
public static<E> void printPreOrder(CTreeNode<E> root)
{
if (root == null) // Nichts zu tun
return;
System.out.println(root.mData); // Verarbeitung des Knotens
printPreOrder(root.mLeft);
printPreOrder(root.mRight);
}
Analog für Inorder und Postorder
Prof. Dr. Manh Tien Tran
19
4 Bäume
Implementierungen
2. Möglichkeit: Mit Hilfe von Queue für Levelorder – iterative Methode
public static <E> void printLevelOrder(CTreeNode<E> root)
{
if (root == null)
return;
// Wir verwenden eine Queue für die Knoten
CListQueue<CTreeNode<E>> q = new CListQueue<CTreeNode<E>>();
// Die Queue wird initialisiert mit dem Element der Stufe 0
q.add(root);
Prof. Dr. Manh Tien Tran
20
4 Bäume
do {
// erster Knoten aus der Queue entfernen
CTreeNode<E> current = q.get();
// und die Kinder in die Queue hinzufügen – Nächste Level!
if (current.mLeft != null)
q.add(current.mLeft);
if (current.mRight != null)
q.add(current.mRight);
System.out.println(current.mData);
} while (q.isEmpty() == false);
}
Prof. Dr. Manh Tien Tran
21
4 Bäume
Iterative Methode – Beispiel für Inorder. Mit Stack
public static <E> void printInorder(CTreeNode<E> root)
{
if (root == null)
return; // nichts zu tun
// Wir verwenden einen Stack (um die Rekursion zu "simulieren"
CListStack<CTreeNode<E>> stack = new CListStack<CTreeNode<E>>();
CTreeNode<E> current = root;
Prof. Dr. Manh Tien Tran
22
4 Bäume
while (true) {
while (current != null) {
stack.push(current);
// steigt soweit wie möglich nach links ab und
current = current.getLeft(); // legt alle Knoten auf den Stack
}
if (stack.isEmpty()) break;
// stop falls der Stack leer ist
current = stack.pop(); // das oberste Element entnehmen und verarbeiten
System.out.println(current.getData());
current = current.getRight(); // Fortsetzung mit dem rechten Unterbaum
}
}
Prof. Dr. Manh Tien Tran
23
4 Bäume
Fädelung von verketteten Binärbäumen
Rekursive Durchlaufalgorithmen sind "relativ teuer" – iterative sind effizienter,
erfordern aber mehr Programmieraufwand.
=> Technik der Fädelung: Man versucht, die Baumknoten in der Reihenfolge der
gewünschten Durchlaufordnung zu verknüpfen.
Idee
●
Jeder Knoten mit Grad < 2 hat freie Verweise, die zur Fädelung verwendet
werden können (=> Siehe Literatur).
Probleme
●
Nur ein Durchlauftyp wird bevorzugt.
●
Hoher Aufwand, wenn sich der Baum ständig ändert.
Prof. Dr. Manh Tien Tran
24
4 Bäume
4.3 Binäre Suchbäume
Ziele
●
Effiziente Verarbeitung großer geordneter Datenbestände
●
Effiziente Suchoperationen
●
Sortierte Verarbeitung des Datenbestandes
Forderungen
●
Auf einzelne Datensätze wird mittels Schlüssels (entweder Teil der Daten
oder Extradaten) zugegriffen.
●
Auf den Schlüsselwerten ist eine Ordnung definiert.
●
Die Schlüsselwerte sind eindeutig im gesamten Datenbestands.
Prof. Dr. Manh Tien Tran
25
4 Bäume
Voraussetzung
Im Folgenden nehmen wir an, dass die Schlüssel vom Typ int sind.
Eigenschaften von binären Suchbäumen
●
●
●
●
Leerer Baum ist auch ein Suchbaum.
Alle Schlüssel der Datenobjekte im linken Unterbaum von B sind kleiner als
der Schlüssel in der Wurzel von B.
Alle Schlüssel der Datenobjekte im rechten Unterbaum von B sind größer als
das in der Wurzel von B.
Der linke und der rechte Unterbaum von B sind jeweils auch binäre
Suchbäume.
Prof. Dr. Manh Tien Tran
26
4 Bäume
Grundoperationen auf binären Suchbäumen
●
Einfügen eines Knotens
●
Suchen eines Knotens
●
Sortierte Ausgabe aller Knoten
●
Löschen eines Knotens
4.3.1 Datenstruktur
public class CBinSearchTree<E>
{
// Referenz auf die Wurzel des binären Suchbaums
private CTreeNode<E> mRoot = null;
Prof. Dr. Manh Tien Tran
27
4 Bäume
4.3.2 Konstruktor
/** Konstruktor - Ein leerer Baum wird erzeugt */
public CBinSearchTree() {}
4.3.3 Einfügen eines Knotens
Vorgehensweise
●
●
●
●
Neue Knoten werden immer als Blätter eingefügt
Die Position des Blattes wird durch den Schlüssel des neuen Knotens (bzw.
durch die Vergleiche mit dem neuen Knoten) festgelegt.
Beim Aufbau eines Baumes wird der erste Knoten die Wurzel
Hat der Baum schon Knoten, dann wird zuerst durch Vergleiche die Position
des Vaterknotens bestimmt. Der neue Knoten wird dann als Kind hinzugefügt.
Prof. Dr. Manh Tien Tran
28
4 Bäume
Beispiele
Fall1 – lexikographische Ordnung "Schlüssel vom Typ String"
Einfüge-Reihenfolge: Melone, Apfel, Banane, Ananas, Clementine, Trauben,
Mango, Kiwi, Orange
Melone
Trauben
Apfel
Ananas
Orange
Banane
Clementine
Mango
Prof. Dr. Manh Tien Tran
Kiwi
29
4 Bäume
Fall2 – lexikographische Ordnung
Einfüge-Reihenfolge: Mango, Apfel, Banane, Ananas, Clementine, Melone, Kiwi,
Orange, Trauben
Mango
Melone
Apfel
Ananas
Banane
Orange
Trauben
Clementine
Kiwi
Prof. Dr. Manh Tien Tran
30
4 Bäume
Bemerkungen
Die Reihenfolge des Einfügens bestimmt das Aussehen des Baumes
●
Bei sortierter Reihenfolge entartet der binäre Suchbaum zu einer linearen
Liste.
●
public boolean insert(int k, E e) {
// erster Fall: der Baum ist leer
if (mRoot == null) {
// Neuer Knoten wird die Wurzel des Baumes
mRoot = new CTreeNode<E>(k, e, null, null);
return true;
}
Prof. Dr. Manh Tien Tran
31
4 Bäume
// Baum ist nicht leer, wir müssen die richtige Position suchen
// Hier verwenden wir zwei Referenzen
CTreeNode<E> parent = null;
CTreeNode<E> child = mRoot;
while (child != null)
{ // wir gehen nach unten - Update die Referenz
parent = child;
// Vergleiche die Schlüssel
int cmp = key - child.getKey();
Prof. Dr. Manh Tien Tran
32
4 Bäume
if (cmp < 0)
// nach links, weil kleiner
child = child.getLeft();
else if (cmp == 0) // Objekt schon im Baum => liefert false zurück
return false;
else
// nach rechts weil größer
child = child.getRight() ;
}
if (key - parent.getKey() < 0) parent.setLeft(new CTreeNode<E>(key, e));
else parent.setRight(new CTreeNode<E>(key, e))
return true;
} // end of insert
Prof. Dr. Manh Tien Tran
33
4 Bäume
Alternativ (Rekursion)
public boolean insertRec(int key, E e)
{ // Baum ist leer => Wurzel ist der neue Knoten
if (mRoot == null)
{
mRoot = new CTreeNode<E>(key, e, null, null);
return true;
}
// Aufruf einer Rekursion
return insertNodeRec(mRoot, key, e);
}
Prof. Dr. Manh Tien Tran
34
4 Bäume
private static <E> boolean insertNodeRec(CTreeNode<E> root, int key, E e) {
int cmp = key - root.getKey() ;
// Ergebnis des Vergleichs
if (cmp == 0) return false;
// Gleich => false zurückgeben
else if (cmp < 0) {
// < 0 => links
if (root.getLeft() == null) {
// links ist noch frei => Einfügen
root.setLeft(new CTreeNode<E>(key, e));
return true;
}
else
// links ist nicht frei => Einfügen
return insertNodeRec(root.getLeft(), key, e); // in den linken Unterbaum
}
Prof. Dr. Manh Tien Tran
35
4 Bäume
else {
if (root.getRight() == null) {
// rechts weil größer
// rechts ist frei? Ja => Einfügen
root.setRight(new CTreeNode<E>(key, e));
return true;
}
else
// rechts belegt => weiter mit dem
return insertNodeRec(root.getRight(), key, e);
// rechten Unterbaum
}
}
Prof. Dr. Manh Tien Tran
36
4 Bäume
4.3.4 Suchen eines Knotens
Vorgehensweise
●
●
●
Die Position des Knotens wird nach dem gleichen Verfahren wie beim
Einfügen eines Knotens gesucht.
Die Suchmethode liefert das Objekt zurück, falls das Element mit dem
angegebenen Schlüssel gefunden wird, null sonst.
Man kann sowohl iterativ als auch rekursiv formulieren. In diesem Fall ist die
Rekursion schlechter!
Prof. Dr. Manh Tien Tran
37
4 Bäume
public E find(int k) {
CTreeNode<E> tmp = mRoot;
while (tmp != null) {
int cmp = k - tmp.getKey();
if (cmp < 0)
tmp = tmp.getLeft();
else if (cmp == 0)
return tmp.getData();
else
tmp = tmp.getRight();
}
return null;
}
Prof. Dr. Manh Tien Tran
38
4 Bäume
public static <E> E findRek(CTreeNode<E> root, int k) {
if (root == null) return null;
int cmp = k - root.getKey();
if (cmp < 0)
return findRek(root.getLeft(), k);
else if (cmp == 0) return root.getData();
else
return findRek(root.getRight(), k);
}
public E findRek(int key)
{
return findRek(mRoot, key);
}
Prof. Dr. Manh Tien Tran
39
4 Bäume
4.3.5 Sortierte Ausgaben aller Knoten
Vorgehensweise
Ein Inorder-Durchlauf des binären Suchbaums liefert eine nach aufsteigenden
Schlüsselwerten sortierte Liste der Knoten
Melone
Beispiel
Trauben
Apfel
Ananas
Banane
Orange
Ananas, Apfel, Banane, Melone, Orange, Trauben
Prof. Dr. Manh Tien Tran
40
4 Bäume
public String toString()
{
if (mRoot == null)
return "[]";
StringBuilder s = new StringBuilder("[");
toStringRek(s, mRoot); // siehe unten
// ein Komma am Ende zu viel => Ersetz es durch ]
s.setCharAt(s.length()-1, ']');
return s.toString();
}
Prof. Dr. Manh Tien Tran
41
4 Bäume
private static<E> void toStringRek(StringBuilder s, CTreeNode<E> root)
{
if (root == null)
return;
toString(s, root.getLeft());
s.append(root.getData() + ",");
toString(s, root.getRight());
}
Prof. Dr. Manh Tien Tran
42
4 Bäume
4.3.6 Löschen eines Knotens
Vorgehensweise
●
●
Die Position des zu löschenden Knotens wird nach dem gleichen Verfahren
wie beim Einfügen eines Knotens gesucht.
Beim Löschen eines Knotens muss man 3 Fälle unterscheiden
Fall 1 Der Knoten ist ein Blatt => Lösche diesen Knoten, keine weitere
Operationen
zu löschender Knoten
Prof. Dr. Manh Tien Tran
43
4 Bäume
Fall 2 Der Knoten ist ein innerer Knoten, der entweder einen leeren linken oder
einen leeren rechten Unterbaum besitzt => Der zu löschende Knoten wird entfernt
und durch den Wurzelknoten des nicht-leeren Unterbaum ersetzt.
zu löschender Knoten
Prof. Dr. Manh Tien Tran
44
4 Bäume
Fall 2
zu löschender Knoten
Prof. Dr. Manh Tien Tran
45
4 Bäume
Fall 3 Der zu löschende Knoten ist ein innerer Knoten mit zwei nicht leeren
Unterbäumen.
Problem: Wo werden die beiden Unterbäume nach dem Löschen des Knotens
angehängt?
Sei K der zu löschenden Knoten
Lösung1: Sei Kr der Knoten mit dem kleinsten Schlüssel im rechten Unterbaum
von K.
●
Vertausche die Daten von K und Kr
●
Lösche den Knoten Kr => Fall 1 oder 2
Lösung 2: Wie Lösung 1 aber mit dem größten Element im linken Unterbaum
D.h. Fall 3 wird auf Fall 1 oder 2 zurückgeführt.
Prof. Dr. Manh Tien Tran
46
4 Bäume
Daten tauschen
wird später gelöscht (Fall 1 oder 2)
Prof. Dr. Manh Tien Tran
47
4 Bäume
Beispiel
Mango
Melone
Apfel
Ananas
Banane
Orange
Trauben
Clementine
Kiwi
Löschen Kiwi : Fall 1
Prof. Dr. Manh Tien Tran
48
4 Bäume
Beispiel (Fortsetzung)
Mango
Melone
Apfel
Ananas
Banane
Clementine
Orange
Trauben
Löschen Orange : Fall 2
Prof. Dr. Manh Tien Tran
49
4 Bäume
Beispiel (Fortsetzung)
Mango
Melone
Apfel
Ananas
Banane
Trauben
Clementine
Fall 3: Lösche Apfel (=> Apfel mit Bananr vertauschen, und wie vorhin löschen)
Prof. Dr. Manh Tien Tran
50
4 Bäume
Beispiel (Fortsetzung)
Ergebnis:
Mango
Banane
Ananas
Prof. Dr. Manh Tien Tran
Clementine
Melone
Trauben
51
4 Bäume
Wichtiger Sonderfall: Zu löschenden Knoten ist die Wurzel.
In Fällen 1 und 2 benötigen wir zwei Referenzen: Eine auf den Vater und eine auf
den zu löschenden Knoten.
Fall 1 vater == null => die Wurzel wird gelöscht
mRoot
mRoot
Prof. Dr. Manh Tien Tran
52
4 Bäume
Fall 2 Der zu löschende Knoten ist links vom Vater
vater.setLeft(ersatz)
vater
knoten
ersatz
Prof. Dr. Manh Tien Tran
53
4 Bäume
Fall 3 Der zu löschende Knoten ist rechts vom Vater
vater.setRight(ersatz)
vater
knoten
ersatz
Prof. Dr. Manh Tien Tran
54
4 Bäume
/**
* Suche nach einem Baumknoten, der das Objekt enthält
* @param key
Schlüssel zum Finden
* @return Array mit zwei Elementen: Der Vater und der Knoten mit dem
*
Schlüssel falls gefunden, null sonst
*/
private Object[] findNode(int key) {
// Wir starten mit der Wurzel
CTreeNode<E> parent = null;
CTreeNode<E> current = this.mRoot;
Prof. Dr. Manh Tien Tran
55
4 Bäume
while (current != null)
{
// so lange wir den Schlüssel noch nicht gefunden haben, vergleichen
// wir den Schlüssel mit dem im Knoten abgespeicherten Schlüssel
int compareResult = key - current.getKey();
if (compareResult == 0)
{ // Gleich => return das Ergebnis
return new Object[]{parent, current};
}
Prof. Dr. Manh Tien Tran
56
4 Bäume
else if (compareResult > 0) { // größer => Versuch mit dem rechten Baum
parent = current;
current = current.getRight();
} else { // sonst den linken Unterbaum
parent = current;
current = current.getLeft();
}
}
// Objekt ist nicht im Baum
return null;
} // end of findNode
Prof. Dr. Manh Tien Tran
57
4 Bäume
/**
* Löscht ein Objekt mit dem gegebenen Schlüssel
* @param key
Schlüssel des Objekts zum Löschen
* @return Das zum löschende Objekt (null falls Objekt nicht im Baum ist
*/
public E remove(int key)
{
// wenn der Baum leer ist => null zurückgeben
if (null == this.mRoot) {
return null;
}
Prof. Dr. Manh Tien Tran
58
4 Bäume
// Schritt 1: Den zu löschenden Knoten finden
// wir brauchen dazu zwei Referenzen. Eine auf den zu löschenden Knoten
// und eine auf seinen Vater (um die Referenzen ggf. zu ändern)
Object[] tmp = findNode(key);
if (tmp == null)
{
// Objekt nicht im Baum
return null;
}
Prof. Dr. Manh Tien Tran
59
4 Bäume
// Vorbereitung tmp[0] zeigt auf den Vater, tmp[1] auf den zu löschenden
// Knoten – current zeigt auf den zu löschenden Knoten, parent der Vater
// und replacement den Ersatzknoten (null im Fall 1, bzw. ein normaler
// Knoten im Fall 2. Fall 3 wird zurückgeführt auf Fall 1 bzw. 2
CTreeNode<E> parent = (CTreeNode<E>) tmp[0];
CTreeNode<E> current = ((CTreeNode<E>) tmp[1]);
E ret = current.getData();
boolean links = false;
// Merker, ob Ersatzknoten links oder rechts steht
CTreeNode<E> replacement;
Prof. Dr. Manh Tien Tran
60
4 Bäume
if (current.getLeft() == null)
{ // Fall 1 bzw. Fall 2 - Beachte, dass Fall 1 ein Spezialfall von Fall 2 ist
// (Ersatz ist null im Fall 1)
replacement = current.getRight();
}
else if (current.getRight() == null)
{
replacement = current.getLeft();
}
Prof. Dr. Manh Tien Tran
61
4 Bäume
else {
// Fall 3: Knoten ist ein innerer Knoten mit zwei Elementen =>
// Ersetz den Knoten mit dem Mimimum des rechten Unterbaums
CTreeNode<E> succ = current.getRight();
parent = current;
// das kleinste Element im rechten Unterbaum suchen
while (succ.getLeft() != null)
{
parent = succ;
succ = succ.getLeft();
}
Prof. Dr. Manh Tien Tran
62
4 Bäume
// Daten in knoten übertragen
current.setKey(succ.getKey());
current.setData(succ.getData());
// zu löschenden Knoten ist succ und wird durch seinen rechten Sohn
// (Fall 1 bzw. 2) ersetzt
current = succ;
replacement = current.getRight();
}
Prof. Dr. Manh Tien Tran
63
4 Bäume
// Knoten löschen
if (parent == null) { // Wurzel wird gelöscht
mRoot = replacement;
} else if (parent.getLeft() == current) {
parent.setLeft(replacement);
} else {
parent.setRight(replacement);
}
return ret;
} // end of remove
Prof. Dr. Manh Tien Tran
64
4 Bäume
4.3.7 Kosten der Grundoperationen
●
n sei die Anzahl der Knoten.
●
Kostenmaß: #aufgesuchten Knoten bzw. #Vergleiche
Sortierte Ausgabe aller Knoten
Inorder-Durchlauf => O(n)
Einfügen eines Knotens
Aufwand in Abhängigkeit von der Einfügeposition
=> Weg von der Wurzel bis zu einem Blatt
=> Durch die Höhe h beschränkt
=> Aufwand O(h)
Prof. Dr. Manh Tien Tran
65
4 Bäume
Suchen eines Knotens
Aufwand in Abhängigkeit von der Position
=> Maximaler Suchweg läuft von der Wurzel bis zu einem Blatt
=> Durch die Höhe h beschränkt
=> Aufwand O(h)
Löschen eines Knotens
Schlimmster Fall ist die Suche nach einem Austauschpartner im Fall 3
=> Ebenfalls durch die Höhe h beschränkt
=> Aufwand O(h)
Folgerung
Die Höhe spielt eine entscheidende Rolle bei binären Suchbäumen.
Prof. Dr. Manh Tien Tran
66
4 Bäume
Schlimmster Fall
Der Baum ist entartet (nur rechte oder linke nicht-leere Unterbäume) => O(n)
Bester Fall
Der Baum ist voll. Die Höhe ist etwa log(n) => O(log(n))
Durchschnittliche mittlere Zugriffskosten
(ohne Beweis) Im Mittel O(log(n)) (im Vergleich zu dem Idealfall mit einem
mittleren Aufwand von etwa 39% zu rechnen).
In der meisten Anwendung will man einen gewissen Grad von Ausgeglichenheit
der Suchbäume garantieren.
=> Balancierte binäre Suchbäume
Prof. Dr. Manh Tien Tran
67
4 Bäume
4.4 AVL-Bäume
AVL: Adelson-Velskii, Landis (russische Mathematiker), 1962
Definition
Ein AVL-Baum ist ein binärer Suchbaum mit der Eigenschaft: Für jeden (inneren)
Knoten ist der absolute Betrag der Differenz der Höhen des linken und rechten
Teilbaumes maximal 1.
Die Knoten sei k mit den Unterbäumen left und right. Der Balancierungsfaktor von
k ist definiert durch
b(k) = Höhe(left) – Höhe(right)
Die AVL-Eigenschaft besagt, dass für jeden Knoten k der Balancierungsfaktor
b(k) entweder -1, 0 oder 1 ist.
Prof. Dr. Manh Tien Tran
68
4 Bäume
Beispiel
10
10
7
1
20
9
4
AVL-Baum
Prof. Dr. Manh Tien Tran
7
15
4
1
20
9
5
kein AVL-Baum
69
4 Bäume
4.4.1 Datenstruktur
class CAVLTreeNode<E> {
private int mKey;
// wird verwendet für binäre Suchbäume
private E mData;
// Verweis auf Datenobjekt
private int mBF;
// Balancierungsfaktor
private CAVLTreeNode<E> mLeft; // Verweis auf den linken Unterbaum
private CAVLTreeNode<E> mRight; // Verweis auf den rechten Unterbaum
...
Prof. Dr. Manh Tien Tran
70
4 Bäume
4.4.2 Grundoperationen auf AVL-Bäumen
●
●
●
●
Einfügen eines Knotens => erfordert ggf. Reorganisationen
Suchen eines Knotens => wie bei binären Suchbäumen (hier wird garantiert,
dass der Suchaufwand O(log(n)) ist)
Sortierte Ausgaben eines Knotens => wie bei binären Suchbäumen (Aufwand
O(n))
Löschen eines Knotens=> erfordert ggf. Reorganisationen
Wichtig: Die maximale Höhe eines AVL-Baums ist immer O(log(n))
Aufwand für Einfügen und Löschen ist somit O(log(n))
Prof. Dr. Manh Tien Tran
71
4 Bäume
Einfügen in AVL-Bäumen
Vorgehensweise:
●
●
●
Durchlaufe von der Wurzel zur Bestimmung der Einfügeposition und füge den
neuen Knoten als Blatt ein.
Nach dem Einfügen eines neuen Knotens können die Balancen der Knoten
auf dem Pfad von der Wurzel bis zum neuen Knoten geändert werden.
Die Verletzung der AVL-Eingenschaften muss durch sogenannte Rotation
behoben werden. Zu betrachtende Fälle sind:
–
Einfügen im linken Teilbaum des linken Kindes (LL)
–
Einfügen im rechten Teilbaum des linken Kindes (RL)
–
Einfügen im rechten Teilbaum des rechten Kindes (RR)
–
Einfügen im linken Teilbaum des rechten Kindes (LR)
Prof. Dr. Manh Tien Tran
72
4 Bäume
Beispiel
Im Knoten stehen Schlüssel / Balancierter Faktor
1010/ 1
7/1
7
1/-1
1
10 / 2
20/1
20
9/0
9
15/0
15
4/0
4
1/-2
15/0
9/0
4/1
5/0
Prof. Dr. Manh Tien Tran
20/1
7/2
5/0
73
4 Bäume
Beispiel
Balancieren
10 / 2
20/1
7/2
1/-2
10 / 1
15/0
9/0
4/0
1/0
4/1
20/1
7/1
9/0
15/0
5/0
5/0
Prof. Dr. Manh Tien Tran
74
4 Bäume
Für das Ausgleichen wird ausgehend von eingefügten Knoten k0 ein Knoten k1
auf dem Pfad zur Wurzel gesucht, dessen Großvater k3 als erster unbalanciert ist.
1. Fall Rotationstyp LL (einfache Rotation)
Einfügen in linken Teilbaum des linken Kindes.
k3 / 2
k2/ 1
k2 / 0
k1 / 0
k3 / 0
k1 / 0
Prof. Dr. Manh Tien Tran
75
4 Bäume
Fall 3 Rotationstyp RR (einfache Rotation)
Einfügen in rechten Teilbaum des rechten Kindes
k3 / -2
k2 / 0
k3 / 0
k2/ -1
k1 / 0
k1 / 0
Prof. Dr. Manh Tien Tran
76
4 Bäume
Fall 2 Rotationstyp LR (doppelte Rotation)
Einfügen in rechten Teilbaum des linken Kindes
k3 / 2
k1 / 0
L
k2 / 0
k2/ -1
k3 / 0
R
k1 / 0
Prof. Dr. Manh Tien Tran
77
4 Bäume
Fall 4 Rotationstyp RL (doppelte Rotation)
Einfügen in linken Teilbaum des rechten Kindes
k3 / -2
k1 / 0
R
k2/ 1
k3 / 0
k2 / 0
L
k1 / 0
Prof. Dr. Manh Tien Tran
78
4 Bäume
Beispiel
Einfügen 2, 4, 11 , 10
2
2/0
4
2/-1
11
4/0
10
4/-1
2/0
4/-1
10
11/1
RR
2/-2
10/0
11/2
10/1
2/0
11/0
4/-2
2/0
4/0
LL
11/0
4/-1
2/0
10/0
9/ 0
11/0
9/0
Prof. Dr. Manh Tien Tran
79
4 Bäume
Beispiel (Fortsetzung) Einfügen 6 , 1 , 3
6
4/-2
10/1
2/0
9/ 1
6/ 0
RL
11/0
1
1/ 0
Prof. Dr. Manh Tien Tran
9/0
4/0
2/ 0
6/ 0
10/-1
6/ 0
11/0
3
9/1
4/1
2/ 1
6
10/-1
9/1
4/1
2/ 0
11/0
1/ 0
6/ 0
10/-1
11/0
3/ 0
80
4 Bäume
Beispiel (Fortsetzung) Einfügen 5, 7
5
9/1
4/0
2/ 0
1/ 0
3/ 0
10/-1
6/ 1
5/ 0
Prof. Dr. Manh Tien Tran
7
9/1
4/0
2/ 0
11/0
1/ 0
3/ 0
10/-1
6/ 0
5/ 0
11/0
7/ 0
81
4 Bäume
Beispiel (Fortsetzung) Einfügen 8
8
9/2
4/-1
2/ 0
1/ 0
3/ 0
6/0
10/-1
6/ -1
5/ 0
4/-1
2/ 0
11/0
7/ -1
1/ 0
3/ 0
5/ 0
9/0
7/ -1
10/-1
8/ 0
11/0
8/ 0
Prof. Dr. Manh Tien Tran
82
4 Bäume
Löschen in AVL-Bäumen
Vorgehensweise:
●
●
Durchlaufe von der Wurzel aus einem Suchpfad zur Bestimmung der
Löschposition und lösche den Knoten (=> Löschpfad)
Höhenänderung von Unterbäumen möglich, deren Wurzeln auf dem
Löschpfad liegen. Nur die Knoten auf diesem Pfad kann das AVL-Kriterium
verletzt werden,
Ablauf:
●
●
●
Zuerst "normales Löschen" (wie bei binären Suchbäumen).
Kritischen Knoten bestimmen (nähester Vorgänger mit BF = +2 oder -2.
Dieser ist Ausgangspunkt der Rotation.
Entlang des Löschpfad (bis zur Wurzel) müssen ggf. Rotationen durchgeführt
werden.
Prof. Dr. Manh Tien Tran
83
4 Bäume
6/0
Beispiel Löschen von 9
4/-1
10/0
Tauschen
6/0
4/-1
2/ 0
1/ 0
5/ 0
3/ 0
2/ 0
9/0
7/ -1
1/ 0
5/ 0
11/0
6/0
4/-1
11/0
2/ 0
1/ 0
Prof. Dr. Manh Tien Tran
9/-1
8/ 0
3/ 0
10/-1
8/ 0
7/ -1
3/ 0
5/ 0
10/1
7/ -1
11/0
8/0
84
4 Bäume
7/0
Beispiel Löschen von 6
4/1
10/1
Tauschen
6/0
4/1
2/ 0
1/ 0
2/ 0
10/1
5/ 0
3/ 0
7/ -1
1/ 0
5/ 0
11/0
8/0
3/ 0
7/1
11/0
4/1
8/0
2/ 0
1/ 0
Prof. Dr. Manh Tien Tran
6/ -1
10/0
5/ 0
8/0
11/0
3/ 0
85
4 Bäume
Beispiel Löschen von 1, 5
7/1
4/1
2/ 0
1/ 0
7/1
4/1
10/0
5/ 0
3/ 0
8/0
11/0
2/ -1
2/ -1
5/ 0
8/0
3/ 0
7/1
4/2
10/0
7/0
10/0
8/0
3/0
11/0
11/0
2/ 0
10/0
4/ 0
8/0
11/0
3/ 0
Prof. Dr. Manh Tien Tran
86
4 Bäume
Beispiel Lösche 11, 8 und 10
7/0
7/1
3/0
2/ 0
10/0
4/ 0
8/0
Lösche 11 und 8
11/0
2/ 0
3/0
10/0
4/ 0
3/0
7/2
3/0
Lösche 10
2/ 0
Prof. Dr. Manh Tien Tran
LL Rotation
4/ 0
2/ 0
7/2
4/ 0
87
4 Bäume
4.5 Vergleiche (worst-case)
sortiertes
Array
verkettete
Liste
normaler
binärer
Suchbaum
AVL-Baum
Suchen
O(log n)
O(n)
O(log n)
O(log n)
Ausgabe
aller Knoten
O(n)
O(n)
O(n)
O(log n)
Einfügen
O(n)
O(n)
O(n)
O(log n)
Löschen
O(n)
O(n)
O(n)
O(log n)
Direkter
Zugriff
O(1)
O(n)
O(n)
O(n)
Prof. Dr. Manh Tien Tran
88
4 Bäume
4.5 Wichtige Variante
Ein Rot-Schwarz-Baum ist ein binärer Suchbaum, in dem jeder Knoten eine
Zusatzinformation – seine Farbe – trägt. Dabei gelten:
1)
Jeder Knoten im Baum ist entweder rot oder schwarz.
2)
Die Wurzel des Baums ist schwarz.
3)
Alle leeren Knoten (null) sind schwarz.
4)
Ist ein Knoten rot, so sind beide Kinder schwarz.
5)
Jeder Pfad von einem gegebenen Knoten zu seinen Blattknoten enthält die
gleiche Anzahl schwarzer Knoten (Schwarzhöhe/Schwarztiefe).
(siehe z.B. wikipedia.de bzw. Implementierung in JDK)
Wichtige Eigenschaft: Längster Pfad ist max. doppelt so lang wie kürzester Pfad.
Prof. Dr. Manh Tien Tran
89
Herunterladen