Teil 7: Bäume Beispiele (1)
Teil 7: Bäume Beispiele (1)
Teil 7: Bäume Beispiele (1)
Erfolgreiche ePaper selbst erstellen
Machen Sie aus Ihren PDF Publikationen ein blätterbares Flipbook mit unserer einzigartigen Google optimierten e-Paper Software.
<strong>Teil</strong> 7:<br />
<strong>Bäume</strong><br />
•! <strong>Beispiele</strong><br />
•! Definition und Eigenschaften<br />
•! Implementierungen<br />
•! Durchlaufen von <strong>Bäume</strong>n<br />
•! Binäre Suchbäume<br />
O. Bittel; Sept. 2008 Programmiertechnik 2 - <strong>Bäume</strong> 7-1<br />
<strong>Beispiele</strong> (1)<br />
<strong>Bäume</strong> gehören zu den wichtigsten Datenstrukturen in der Informatik.<br />
Entscheidungsbäume<br />
Minimum von<br />
3 Zahlen<br />
x ! y<br />
1 0<br />
Entscheidungsknoten<br />
x ! z<br />
y ! z<br />
1 0 1 0<br />
min=x min=z min=y min=z<br />
Aktionsknoten<br />
Syntaxbäume<br />
Syntaxbaum für den<br />
arithmetischen Ausdruck<br />
(a - b) * c<br />
*<br />
- c<br />
a<br />
b<br />
O. Bittel; Sept. 2008 Programmiertechnik 2 - <strong>Bäume</strong> 7-2
<strong>Beispiele</strong> (2)<br />
Verzeichnis-Strukturen<br />
Ausschnitt aus einer<br />
Unix-Verzeichnis-Struktur<br />
/<br />
Wurzelverzeichnis<br />
bin<br />
etc<br />
usr<br />
dev<br />
ls date who passwd src bin tty00 tty01<br />
date.c<br />
who.c<br />
Verzeichnisknoten<br />
Dateiknoten<br />
O. Bittel; Sept. 2008 Programmiertechnik 2 - <strong>Bäume</strong> 7-3<br />
<strong>Bäume</strong>: Definition und Begriffe (1)<br />
<strong>Bäume</strong> lassen sich am einfachsten rekursiv definieren:<br />
(1)! Ein einzelner Knoten<br />
ist ein Baum.<br />
(2)! Falls B 1 , B 2 , …, B n (n " 1) <strong>Bäume</strong> sind, dann ist auch<br />
B 1 B 2<br />
B n<br />
Wurzel<br />
<strong>Teil</strong>bäume<br />
ein Baum.<br />
Hier hängen n <strong>Bäume</strong> an einem Knoten, der auch Wurzel genannt wird.<br />
Die Verbindungslinien heißen Kanten.<br />
Die <strong>Bäume</strong> B 1 , B 2 , …, B n werden auch <strong>Teil</strong>bäume genannt.<br />
(In der Informatik wachsen also <strong>Bäume</strong> in anderer Richtung als in der Natur!)<br />
(3)! Außerdem wollen wir auch die leere Menge als Baum (leerer Baum) definieren.<br />
O. Bittel; Sept. 2008 Programmiertechnik 2 - <strong>Bäume</strong> 7-4
<strong>Bäume</strong>: Definition und Begriffe (2)<br />
Beispiel:<br />
Dieser Baum entsteht durch<br />
•! mehrmaliges Anwenden der Regel (1) und<br />
•! 2-maliges Anwenden der Regel (2)<br />
Weitere Begriffe:<br />
•!<br />
•!<br />
•!<br />
•!<br />
Die Knoten unterhalb eines Knoten k werden auch<br />
Kindknoten und k Elternknoten genannt.<br />
Knoten ohne Kinder werden Blätter genannt.<br />
Ein Baum heißt geordnet, falls die Reihenfolge der<br />
Kinder eine Rolle spielt<br />
(s. Bsp. Syntaxbaum für arithm. Ausdruck)<br />
Üblicherweise enthalten die Knoten Daten.<br />
Man spricht dann von einem markierten Baum.<br />
…<br />
…<br />
Elternknoten<br />
Kindknoten<br />
Blätter<br />
O. Bittel; Sept. 2008 Programmiertechnik 2 - <strong>Bäume</strong> 7-5<br />
<strong>Bäume</strong>: Definition und Begriffe (3)<br />
Weitere Begriffe:<br />
•!<br />
Die Tiefe eines Knotens ist sein Abstand zur Wurzel,<br />
d.h. die Anzahl der Kanten von der Wurzel zu dem Knoten.<br />
•!<br />
•!<br />
Die Menge aller Knoten der Tiefe i wird auch i-te Ebene<br />
(level) genannt.<br />
Die Höhe eines Baums ist die maximale Tiefe,<br />
die von einem Knoten erreicht werden kann.<br />
Beispiel<br />
Ein Baum der<br />
Höhe 2.<br />
Ebene 0<br />
Ebene 1<br />
Ebene 2<br />
O. Bittel; Sept. 2008 Programmiertechnik 2 - <strong>Bäume</strong> 7-6
Definition<br />
Binärbäume<br />
Ein Binärbaum ist ein geordneter Baum, bei dem jeder Knoten maximal 2 Kinder hat.<br />
Die beiden Kinder werden linkes und rechtes Kind genannt.<br />
Hat ein Knoten nur ein Kind, dann muss es entweder ein linkes oder ein rechtes Kind sein.<br />
<strong>Beispiele</strong><br />
a<br />
b<br />
B 1 B 2<br />
B 3<br />
<strong>Bäume</strong> B 2 und B 3 sind strukturell unterschiedlich:<br />
in B 2 hat Knoten a einen linken Sohn während in<br />
B 3 Knoten b nur einen rechten Sohn hat.<br />
B 4<br />
O. Bittel; Sept. 2008 Programmiertechnik 2 - <strong>Bäume</strong> 7-7<br />
Definition<br />
Vollständige Binärbäume<br />
Ein vollständiger Binärbaum ist ein Binärbaum, bei der jeder Ebene (bis auf die letzte)<br />
vollständig gefüllt und die letzte Ebene von links nach rechts gefüllt ist.<br />
<strong>Beispiele</strong><br />
Ebene 0<br />
Ebene 1<br />
Eigenschaften:<br />
•!<br />
B 1<br />
Ein vollständiger Binärbaum der Höhe h mit vollständig gefüllter letzter Ebene hat<br />
n = 2 0 + 2 1 + … + 2 h = 2 h+1 – 1<br />
viele Knoten (vergleiche Baum B 3 ). Damit gilt auch<br />
h = log 2 (n+1)-1 = #log 2 n$ (abgerundet!)<br />
B 2<br />
B 3<br />
Ebene 2<br />
Ebene 3<br />
•!<br />
Ein beliebiger vollständiger Binärbaum mit n Knoten (vergleiche Baum B 2 ) hat die Höhe<br />
h = #log 2 n$<br />
O. Bittel; Sept. 2008 Programmiertechnik 2 - <strong>Bäume</strong> 7-8
<strong>Teil</strong> 7:<br />
<strong>Bäume</strong><br />
•! <strong>Beispiele</strong><br />
•! Definition und Eigenschaften<br />
•! Implementierungen<br />
•! Durchlaufen von <strong>Bäume</strong>n<br />
•! Binäre Suchbäume<br />
O. Bittel; Sept. 2008 Programmiertechnik 2 - <strong>Bäume</strong> 7-9<br />
Implementierung von Binärbäumen<br />
Implementierung mit verketteten Knoten<br />
Jeder Knoten hat jeweils einen Zeiger<br />
für das linke bzw. rechte Kind.<br />
root<br />
A<br />
A<br />
B<br />
C<br />
B<br />
C<br />
D<br />
E<br />
F<br />
D E F<br />
G<br />
H<br />
I<br />
G<br />
H<br />
I<br />
struct Node<br />
{<br />
int data;<br />
Node* left; // linkes Kind<br />
Node* right; // rechtes Kind<br />
};<br />
O. Bittel; Sept. 2008 Programmiertechnik 2 - <strong>Bäume</strong> 7-10
Implementierung von beliebigen <strong>Bäume</strong>n<br />
Implementierung mit verketteten Knoten<br />
Jeder Knoten enthält 2 Zeiger:<br />
•! Ein Zeiger auf das erste Kind<br />
•! Ein Zeiger für den rechts liegenden<br />
Geschwisterknoten (engl. sibling)<br />
root<br />
A<br />
A<br />
B<br />
C<br />
D<br />
E<br />
F<br />
G<br />
B<br />
C<br />
D E F G<br />
H I J K<br />
H<br />
I<br />
J<br />
K<br />
L<br />
M<br />
L<br />
M<br />
struct Node<br />
{<br />
int data;<br />
Node* firstChild;<br />
Node* nextSibling;<br />
};<br />
O. Bittel; Sept. 2008 Programmiertechnik 2 - <strong>Bäume</strong> 7-11<br />
Implementierung von vollständigen Binärbäumen<br />
Implementierung mit einem Feld<br />
Die Knoten werden ebenenweise in einem Feld abgespeichert.<br />
Ebene 0<br />
Ebene 1<br />
5<br />
8 7<br />
a [0] [1] [2] [3] [4] [5] [6] [7] [8] [9]<br />
5 8 7 1 3 9 2 12 10 6<br />
Ebene 2<br />
1 3 9 2<br />
Ebene 3<br />
12 10 6<br />
Effizienter Zugriff auf Eltern und Kinder:<br />
•! Kinder von a[i]: a[2*i+1] und a[2*i+2] für i " 0<br />
•! Elternknoten von a[i]: a[(i-1)/2] für i " 1 und mit ganzzahliger Division<br />
Beispiel:<br />
•! Kinder von a[3] = 1: a[7] = 12 und a[8] = 10<br />
•! Elternknoten von a[6] = 2: a[(6-1)/2] = a[2] = 7<br />
O. Bittel; Sept. 2008 Programmiertechnik 2 - <strong>Bäume</strong> 7-12
<strong>Teil</strong> 7:<br />
<strong>Bäume</strong><br />
•! <strong>Beispiele</strong><br />
•! Definition und Eigenschaften<br />
•! Implementierungen<br />
•! Durchlaufen von <strong>Bäume</strong>n<br />
•! Binäre Suchbäume<br />
O. Bittel; Sept. 2008 Programmiertechnik 2 - <strong>Bäume</strong> 7-13<br />
Ziel<br />
Überblick<br />
Das Besuchen aller Knoten in einer bestimmten Reihenfolge<br />
ist eine oft benötigte Operation.<br />
Durchlaufreihenfolgen<br />
•! PreOrder: besuche Wurzel, besuche linken <strong>Teil</strong>baum; besuche rechten <strong>Teil</strong>baum;<br />
•! PostOrder: besuche linken <strong>Teil</strong>baum; besuche rechten <strong>Teil</strong>baum; besuche Wurzel;<br />
•! InOrder: besuche linken <strong>Teil</strong>baum; besuche Wurzel; besuche rechten <strong>Teil</strong>baum;<br />
•! LevelOrder: besuche Knoten ebenenweise<br />
Bemerkungen<br />
•!<br />
•!<br />
Die Präfixe Pre, Post bzw. In bedeuten vorher, nachher und dazwischen.<br />
Gemeint ist damit der Zeitpunkt, an dem die Wurzel besucht wird.<br />
Um die Darstellung zu vereinfachen, werden wir die Durchlaufalgorithmen nur für<br />
Binärbäume behandeln.<br />
Die Algorithmen lassen sich in einfacher Weise auf allgemeine <strong>Bäume</strong> übertragen.<br />
Der InOrder-Durchlauf ist jedoch nur für Binärbäume sinnvoll.<br />
O. Bittel; Sept. 2008 Programmiertechnik 2 - <strong>Bäume</strong> 7-14
PreOrder-Durchlauf für Binärbäume<br />
Algorithmus<br />
void preOrder(Node* p)<br />
{<br />
if (p != 0)<br />
{<br />
bearbeite(p->data);<br />
preOrder(p->left);<br />
preOrder(p->right);<br />
}<br />
}<br />
Beispiel<br />
*<br />
- c<br />
// Definition eines Baums:<br />
struct Node<br />
{<br />
int data;<br />
Node* left; // linkes Kind<br />
Node* right; // rechtes Kind<br />
};<br />
Node* root;<br />
…<br />
// Aufruf von preorder<br />
preOrder(root);<br />
Durchlaufreihenfolge:<br />
* - a b c<br />
a<br />
b<br />
O. Bittel; Sept. 2008 Programmiertechnik 2 - <strong>Bäume</strong> 7-15<br />
PostOrder-Durchlauf für Binärbäume<br />
Algorithmus<br />
void postOrder(Node* p)<br />
{<br />
if (p != 0)<br />
{<br />
postOrder(p->left);<br />
postOrder(p->right);<br />
bearbeite(p->data);<br />
}<br />
}<br />
Beispiel<br />
*<br />
- c<br />
Durchlaufreihenfolge:<br />
a b – c *<br />
(Entspricht der sog. Postfix-Notation für<br />
arithmetische Ausdrücke)<br />
a<br />
b<br />
O. Bittel; Sept. 2008 Programmiertechnik 2 - <strong>Bäume</strong> 7-16
InOrder-Durchlauf für Binärbäume<br />
Algorithmus<br />
void inOrder(Node* p)<br />
{<br />
if (p != 0)<br />
{<br />
inOrder(p->left);<br />
bearbeite(p->data);<br />
inOrder(p->right);<br />
}<br />
}<br />
Beispiel<br />
*<br />
- c<br />
Durchlaufreihenfolge:<br />
a – b * c<br />
a<br />
b<br />
O. Bittel; Sept. 2008 Programmiertechnik 2 - <strong>Bäume</strong> 7-17<br />
LevelOrder-Durchlauf für Binärbäume<br />
Algorithmus<br />
Die Knoten werden ebenenweise in einer Schlange (Queue) gespeichert<br />
und in einer while-Schleife abgearbeitet.<br />
void levelOrder(Node* p)<br />
{<br />
Queue queue;<br />
queue.push(p); // Ebene 0<br />
}<br />
while ( !queue.empty() )<br />
{<br />
Node* q;<br />
// Schreibe vorderstes Element aus Schlange<br />
// nach q und lösche Element aus Schlange:<br />
queue.front(q);<br />
queue.remove();<br />
if (q != 0)<br />
{<br />
bearbeite(q->data);<br />
queue.push(q->left);<br />
queue.push(q->right);<br />
}<br />
}<br />
Beispiel<br />
*<br />
- c<br />
a b<br />
Durchlaufreihenfolge:<br />
* – c a b<br />
O. Bittel; Sept. 2008 Programmiertechnik 2 - <strong>Bäume</strong> 7-18
Aufgabe 7.1<br />
Aufgaben<br />
Beschreiben Sie anhand eines Beispiels, wie Sie Verzeichnisstrukturen textuell ausgeben<br />
würden. Welche Durchlaufreihenfolge wählen Sie dabei?<br />
Schreiben Sie einen Algorithmus, um Verzeichnisstrukturen auszugeben.<br />
Aufgabe 7.2<br />
Schreiben Sie einen Algorithmus, um die Speicherplatzgröße einer gesamten<br />
Verzeichnisstruktur zu berechnen.<br />
Es kann davon ausgegangen werden, dass die Speichergröße von jedem<br />
Unterverzeichnis und jeder Datei beim entsprechenden Knoten abgespeichert ist.<br />
/<br />
bin<br />
etc<br />
usr<br />
dev<br />
ls date who passwd src bin tty00 tty01<br />
date.c<br />
who.c<br />
O. Bittel; Sept. 2008 Programmiertechnik 2 - <strong>Bäume</strong> 7-19<br />
<strong>Teil</strong> 7:<br />
<strong>Bäume</strong><br />
•! <strong>Beispiele</strong><br />
•! Definition und Eigenschaften<br />
•! Implementierungen<br />
•! Durchlaufen von <strong>Bäume</strong>n<br />
•! Binäre Suchbäume<br />
O. Bittel; Sept. 2008 Programmiertechnik 2 - <strong>Bäume</strong> 7-20
Knoten<br />
Definition von binären Suchbäumen<br />
Alle Knoten in einem Baum enthalten<br />
einen Schlüssel (Key) und evtl. weitere Daten (Value).<br />
Definition<br />
Ein binärer Suchbaum ist ein Binärbaum, bei dem<br />
für alle Knoten k folgende Eigenschaften gelten:<br />
struct Node<br />
{<br />
KeyType key;<br />
ValueType value;<br />
Node* left; // linkes Kind<br />
Node* right; // rechtes Kind<br />
};<br />
(1)! Alle Schlüssel im linken <strong>Teil</strong>baum sind kleiner als der Schlüssel im Knoten k<br />
(2)! Alle Schlüssel im rechten <strong>Teil</strong>baum sind größer als der Schlüssel im Knoten k<br />
Beachte: Die Schlüssel müssen hier eindeutig sein. Es ist aber auch möglich, dass gleiche<br />
Schlüssel mehrfach vorkommen dürfen, was kleine Änderungen in den Algorithmen erfordert.<br />
<strong>Beispiele</strong><br />
7<br />
2 8<br />
6<br />
2 8<br />
1<br />
3<br />
Degenerierter<br />
Suchbaum<br />
1 4<br />
4<br />
4<br />
3 6<br />
3<br />
In den <strong>Bäume</strong>n sind nur<br />
die Schlüssel dargestellt.<br />
O. Bittel; Sept. 2008 Programmiertechnik 2 - <strong>Bäume</strong> 7-21<br />
7<br />
Suchen in binären Suchbäumen<br />
Operation searchR<br />
bool searchR(KeyType k, ValueType& v, const Node* p);<br />
Suche nach einem Knoten mit Schlüssel k im <strong>Teil</strong>baum p.<br />
Falls gefunden, wird der im Knoten abspeicherte Datenwert v und<br />
der Rückgabewert true zurückgeliefert.<br />
Falls nicht gefunden, wird der Rückgabewert false zurückgeliefert.<br />
Algorithmus<br />
Beispiel<br />
bool searchR(KeyType k, ValueType& v, const Node* p)<br />
{<br />
p 7<br />
if (p == 0)<br />
R steht für<br />
return false;<br />
rekursiv<br />
else if (k < p->key)<br />
2 8<br />
return searchR(k, v, p->left);<br />
else if (k > p->key)<br />
return searchR(k, v, p->right);<br />
else // k gefunden<br />
{<br />
1 4<br />
v = p->value;<br />
return true;<br />
3 6<br />
}<br />
} searchR(3,v,p)<br />
O. Bittel; Sept. 2008 Programmiertechnik 2 - <strong>Bäume</strong> 7-22
Idee<br />
Einfügen in binären Suchbäumen (1)<br />
Um einen Schlüssel k einzufügen, wird zunächst nach dem Schlüssel k gesucht.<br />
Falls der einzufügende Schlüssel k nicht bereits im Baum vorkommt, endet die Suche<br />
erfolglos bei einem 0-Zeiger.<br />
An dieser Stelle wird dann ein neuen Knoten mit Schlüssel k eingefügt.<br />
Beispiel 1: füge 6 ein<br />
Beispiel 2: füge 8 ein<br />
7<br />
7<br />
7<br />
7<br />
2 9<br />
2<br />
9<br />
2<br />
9<br />
2<br />
9<br />
1 4<br />
1<br />
4<br />
1<br />
4<br />
1<br />
4<br />
8<br />
3<br />
3<br />
6<br />
3<br />
6<br />
3<br />
6<br />
Suche von 6 endet<br />
bei 0-Zeiger<br />
Ersetzte 0-Zeiger durch<br />
Zeiger auf Knoten 6<br />
Suche von 8 endet<br />
bei 0-Zeiger<br />
Ersetzte 0-Zeiger durch<br />
Zeiger auf Knoten 8<br />
O. Bittel; Sept. 2008 Programmiertechnik 2 - <strong>Bäume</strong> 7-23<br />
Einfügen in binären Suchbäumen (2)<br />
Operation insertR<br />
bool insertR(KeyType k, ValueType v, Node*& p);<br />
Fügt im <strong>Teil</strong>baum p neuen Knoten mit Schlüssel k und Datenwert v ein.<br />
Falls Schlüssel schon vorhanden, wird kein neuer Knoten eingefügt.<br />
Beachte:<br />
Der Parameter p ist ein Ein/Ausgabeparameter und daher als Referenzparameter<br />
realisiert. Der <strong>Teil</strong>baum wird gelesen und geändert.<br />
Algorithmus<br />
bool insertR(KeyType k, ValueType v, Node*& p)<br />
{<br />
if (p == 0) {<br />
p = new Node;<br />
p->key = k; p->value = v;<br />
return true;<br />
}<br />
else if (k < p->key)<br />
return insertR(k,v,p->left);<br />
else if (k > p->key)<br />
return insertR(k,v,p->right);<br />
else // k bereits vorhanden<br />
return false;<br />
}<br />
O. Bittel; Sept. 2008 Programmiertechnik 2 - <strong>Bäume</strong> 7-24<br />
p<br />
3<br />
7<br />
2 9<br />
1 4<br />
p<br />
1<br />
insert(6, v, p)<br />
2<br />
3<br />
7<br />
4<br />
9<br />
6
Löschen in binären Suchbäumen (1)<br />
Idee<br />
Um einen Schlüssel k zu löschen, wird zunächst nach dem Schlüssel k gesucht.<br />
Es sind dann 4 Fälle zu unterscheiden:<br />
Fall „Nicht vorhanden“:<br />
Schlüssel k kommt nicht vor:<br />
dann ist nichts zu tun.<br />
Fall „Keine Kinder“:<br />
Der Schlüssel kommt in einem Blatt vor (keine Kinder):<br />
dann kann der Knoten einfach entfernt werden.<br />
Fall „Ein Kind“:<br />
Der Knoten mit dem gefundenen Schlüssel hat genau ein Kind:<br />
s. nächste Folie<br />
Fall „Zwei Kinder“:<br />
Der Knoten mit dem gefunden Schlüssel hat zwei Kinder<br />
s. übernächste Folie<br />
O. Bittel; Sept. 2008 Programmiertechnik 2 - <strong>Bäume</strong> 7-25<br />
Löschen in binären Suchbäumen (2)<br />
Fall „Ein Kind“<br />
Der zu löschende Knoten k hat genau ein Kind.<br />
Überbrücke den Knoten k, indem der Elternknoten von k auf Kind von k verzeigert wird<br />
(Bypass) und lösche k.<br />
Beispiel: lösche Knoten mit Schlüssel 4<br />
7<br />
7<br />
7<br />
2<br />
9<br />
2<br />
9<br />
2<br />
9<br />
1<br />
4<br />
1<br />
4<br />
1<br />
3<br />
3<br />
3<br />
Suche 4<br />
Knoten 4 wird<br />
überbrückt (Bypass)<br />
Knoten 4 wird<br />
gelöscht<br />
O. Bittel; Sept. 2008 Programmiertechnik 2 - <strong>Bäume</strong> 7-26
Löschen in binären Suchbäumen (3)<br />
Fall „Zwei Kinder“ :<br />
Der zu löschende Knoten k hat zwei Kinder.<br />
1. Ersetze den Knoten k durch den kleinsten Knoten k min im rechten <strong>Teil</strong>baum von k.<br />
2. Lösche dann k min . Da der kleinste Knoten k min im linken <strong>Teil</strong>baum kein linkes Kind hat,<br />
kann das Löschen von k min wie im Fall „Ein Kind“ bzw. „Keine Kinder“ behandelt werden.<br />
Beispiel: lösche Knoten mit Schlüssel 2<br />
7<br />
7<br />
7<br />
7<br />
2<br />
9<br />
2<br />
9<br />
3<br />
9<br />
3<br />
9<br />
1<br />
5<br />
1<br />
5<br />
1<br />
5<br />
1<br />
5<br />
3<br />
6<br />
3<br />
6<br />
3<br />
6<br />
6<br />
4<br />
4<br />
4<br />
4<br />
Suche 2<br />
Suche kleinsten Knoten in<br />
rechten <strong>Teil</strong>baum von 2<br />
Knoten 2 wird durch<br />
kleinsten Knoten ersetzt<br />
kleinster Knoten<br />
wird gelöscht<br />
O. Bittel; Sept. 2008 Programmiertechnik 2 - <strong>Bäume</strong> 7-27<br />
Löschen in binären Suchbäumen (4)<br />
bool removeR(KeyType k, Node*& p)<br />
{<br />
if (p == 0) // k nicht vorhanden<br />
return false;<br />
else if (k < p->key)<br />
return removeR(k,p->left);<br />
else if (k > p->key)<br />
return removeR(k,p->right);<br />
else if (p->left == 0 || p->right == 0) {<br />
Node* temp = p;<br />
if (p->left != 0)<br />
}<br />
p = p->left; // Bypass zu linkes Kind<br />
else<br />
p = p->right; // Bypass zu rechtes Kind<br />
delete temp;<br />
return true;<br />
}<br />
else {<br />
// Min. im rechten <strong>Teil</strong>baum suchen:<br />
Node* min = searchMinR (p->right);<br />
// Zu löschender Knoten durch Min. ersetzen<br />
p->data = min->data; p->key = min->key;<br />
// Min. in rechtem <strong>Teil</strong>baum löschen:<br />
return removeR (min->key, p->right);<br />
}<br />
Operation removeR<br />
Löscht im <strong>Teil</strong>baum p Knoten mit Schlüssel k.<br />
Der Parameter p ist ein Ein/Ausgabeparameter<br />
und daher ein Referenzparameter.<br />
Knoten hat ein Kind<br />
oder kein Kind<br />
Knoten hat<br />
zwei Kinder<br />
O. Bittel; Sept. 2008 Programmiertechnik 2 - <strong>Bäume</strong> 7-28
Löschen in binären Suchbäumen (5)<br />
Operation searchMinR<br />
Sucht im <strong>Teil</strong>baum p nach dem kleinsten Knoten und<br />
liefert Zeiger auf Minimum zurück.<br />
Node* searchMinR(const Node* p)<br />
{<br />
if (p == 0) // Minimum nicht vorhanden<br />
return 0;<br />
else if (p->left == 0) // Minimum gefunden<br />
return p;<br />
else<br />
return searchMinR (p->left);<br />
}<br />
Bemerkung<br />
Beachten Sie, dass in der Operation removeR im Fall „Knoten hat zwei Kinder“ der<br />
Aufruf von searchMinR und der rekursive Aufruf von removeR bedeuten, dass zweimal<br />
vom rechten Kind p->right zum kleinsten Knoten gelaufen wird.<br />
Diese Ineffezienz lässt sich jedoch beheben, indem searchMinR noch zusätzlich das<br />
Löschen des kleinsten Elements übernimmt. Der rekursive Aufruf von removeR ist dann<br />
überflüssig.<br />
Diese Variante findet sich in der Quell-Code-Sammlung zur Vorlesung.<br />
O. Bittel; Sept. 2008 Programmiertechnik 2 - <strong>Bäume</strong> 7-29<br />
Worst-Case<br />
Analyse<br />
Da im schlechtesten Fall ein binärer Suchbaum mit n Knoten zu einem Baum der Höhe<br />
n-1 entarten kann (Bsp?), haben die Operationen zum Suchen, Einfügen und Löschen<br />
eine maximale Suchlänge von n.<br />
Damit: T max (n) = O(n)<br />
Average-Case<br />
In [Ottmann und Widmayer 2002] wird gezeigt, dass die durchschnittliche Laufzeit um<br />
eine Größenordnung besser ist. Es werden zwei Ergebnisse hergeleitet, die sich darin<br />
unterscheiden, welche Verteilung der <strong>Bäume</strong> angenommen wird:<br />
•!<br />
<strong>Bäume</strong> mit n Knoten entstehen durch eine Folge von Einfüge-Operationen von n<br />
unterschiedlichen Elementen. Es wird angenommen, dass jede der n! möglichen<br />
Anordnungen der Elemente gleichwahrscheinlich ist. Gemittelt wird dann über die n!<br />
viele auf diese Weise erzeugten <strong>Bäume</strong>. Man erhält dann für die Such-Operation eine<br />
mittlere Suchlänge von ungefähr 1.4 log 2 n.<br />
Damit: T mit (n) = O(log 2 n)<br />
•!<br />
Es wird angenommen, dass alle strukturell verschiedenen binären Suchbäume mit n<br />
Knoten gleichwahrscheinlich sind. Man erhält dann für die Such-Operation eine<br />
mittlere Suchlänge von ungefähr<br />
Damit: T mit (n) = O( )<br />
O. Bittel; Sept. 2008 Programmiertechnik 2 - <strong>Bäume</strong> 7-30
Aufgabe 7.3<br />
Aufgabe 7.4<br />
Aufgaben (1)<br />
Geben Sie den binären Suchbaum an, nachdem mit einem leeren Baum<br />
begonnen wurde und folgende Operationen durchgeführt worden sind:<br />
•! Einfügen von: 10, 5, 3, 8, 2, 4, 1, 15, 12, 17, 6, 7, 9<br />
•! Löschen von: 2, 3, 5, 12, 10<br />
Beseitigen Sie die beiden endrekursiven Aufrufe in der Funktion searchR.<br />
Aufgabe 7.5<br />
Schreiben Sie eine Funktion, die das größte Element in einem binären Suchbaum<br />
findet und zurückliefert.<br />
Aufgabe 7.6<br />
Schreiben Sie eine Funktion makeEmpty, die den Speicherplatz für einen binären<br />
Suchbaum vollständig freigibt.<br />
Aufgabe 7.7<br />
Schreiben Sie eine Funktion copy, die eine Kopie eines binären Suchbaums erstellt und<br />
zurückliefert.<br />
O. Bittel; Sept. 2008 Programmiertechnik 2 - <strong>Bäume</strong> 7-31<br />
Aufgabe 7.8<br />
Aufgaben (2)<br />
Schreiben Sie eine Funktion save, die einen binären Suchbaum auf eine Datei schreibt<br />
und eine Funktion read, die einen binären Suchbaum von einer Datei einliest.<br />
Es sind 2 Varianten vorzusehen:<br />
a)! Die Funktionen save und read sollen strukturerhaltend sein. D.h. nach<br />
Abspeichern und wieder Einlesen hat der binäre Suchbaum die gleiche Struktur<br />
wie zuvor.<br />
b)! Die Funktionen save und read müssen nicht strukturerhaltend sein. Vielmehr<br />
wird gefordert, dass beim Einlesen ein binärer Suchbaum mit minimaler Höhe<br />
erzeugt wird.<br />
O. Bittel; Sept. 2008 Programmiertechnik 2 - <strong>Bäume</strong> 7-32
Klasse BinarySearchTree<br />
Ziel<br />
Klasse BinarySearchTree, mit der sich eine Menge von Elementen,<br />
bestehend aus Schlüssel und Wert effizient verwalten lassen.<br />
Implementierung als binärer Suchbaum.<br />
Beispiel: Telefonbucheintrag mit Schlüssel = Name und Werte = Telefonnummer.<br />
Schnittstelle<br />
•! search(k, v) Sucht nach Schlüssel k und liefert zugehörigen Wert v<br />
(Ausgabeparameter) zurück<br />
•! insert(k,v) Fügt neues Element mit Schlüssel k und Wert v ein<br />
•! remove(k) Löscht Element mit Schlüssel k<br />
Flexibilität durch Schablonenmechanismus<br />
Der Schlüsseltyp und der Typ der Elementwerte sind parametrisiert.<br />
Damit lassen sich verschiedene Anwendungen einfach realisieren.<br />
<strong>Beispiele</strong>:<br />
// Telefonbuch:<br />
typedef string FamName;<br />
typedef string TelNr;<br />
Schlüssel-Typ<br />
Elementwerte-Typ<br />
// Wörterbuch:<br />
typedef string Deutsch;<br />
typedef string Englisch;<br />
BinarySearchTree < FamName, TelNr> telBuch;<br />
BinarySearchTree wBuch;<br />
O. Bittel; Sept. 2008 Programmiertechnik 2 - <strong>Bäume</strong> 7-33<br />
Öffentlicher <strong>Teil</strong> der Klasse BinärySearchTree<br />
template <br />
// KeyType erfordert Vergleichsoperatoren . ValueType erfordert Zuweisungsoperator =<br />
class BinarySearchTree<br />
{<br />
Schlüssel-Typ<br />
public:<br />
BinarySearchTree() {root = 0;}<br />
Elementwerte-Typ<br />
~BinarySearchTree() {makeEmptyR(root);}<br />
void traverse() const { traverseR(root);}<br />
// Travesiert über alle Schlüssel in sortierter Reihenfolge und gibt Daten aus.<br />
bool search(const KeyType& k, ValueType& v) const { return searchR(k,v,root);}<br />
// Sucht Schlüssel k und liefert Wert v falls gefunden.<br />
// Gibt true zurück, falls Schlüssel k gefunden wird, und sonst false.<br />
bool insert(const KeyType& k, const ValueType& v) { return insertR(k,v,root);}<br />
// Fügt neuen Schlüssel k mit Wert v ein. Falls Schlüssel bereits vorhanden ist,<br />
// wird nicht eingefügt und false zurückgeliefert, sonst true.<br />
bool remove(const KeyType& k) { return removeR(k,root);}<br />
// Löscht Schlüssel k. Falls Schlüssel gelöscht werden konnte wird true zurückgeliefert<br />
// und sonst false (d.h. Schlüssel war nicht vorhanden).<br />
private:<br />
// …<br />
};<br />
O. Bittel; Sept. 2008 Programmiertechnik 2 - <strong>Bäume</strong> 7-34
Privater <strong>Teil</strong> der Klasse BinärySearchTree<br />
template <br />
class BinarySearchTree<br />
{<br />
public:<br />
// …<br />
private:<br />
struct Node<br />
{<br />
KeyType key;<br />
Deklaration eines<br />
ValueType value;<br />
Binärbaums<br />
Node* left;<br />
Node* right;<br />
// Konstruktor<br />
Node(const KeyType& k, const ValueType& v) : key(k), value(v), left(0), right(0) {}<br />
};<br />
Node* root;<br />
};<br />
void traverseR(const Node* p) const;<br />
bool searchR(const KeyType& k, ValueType& v, const Node* p) const;<br />
bool insertR(const KeyType& k, const ValueType& v, Node*& p);<br />
bool removeR(const KeyType& k, Node*& p);<br />
Rekursive, private<br />
Methoden<br />
void getRemoveMin(KeyType& k, ValueType& v, Node*& p); // Hilfsmethode für removeR<br />
void makeEmptyR(Node* p);<br />
O. Bittel; Sept. 2008 Programmiertechnik 2 - <strong>Bäume</strong> 7-35