Programmieren - Nichtlineare Datenstrukturen - fbi.h

Werbung
FB Informatik
Prof. Dr. R.Nitsch
Programmieren - Nichtlineare Datenstrukturen
Reiner Nitsch
 [email protected]
Nichtlineare Dynamische Datenstrukturen
Operation
Suchen
Einfügen
Löschen
Auswählen
Sort. Array
O(log N)
O(N)
O(N)
O(1)
Sortierte physikalische Sequenzen (z.B.
C-Array, vector, deque, …) sind schnell
beim Auswählen (z.B. über Index) bzw.
Suchen (binäre Suche) aber langsam
beim Einfügen bzw. Löschen.
Sort. Liste
O(N)
O(1)
O(1)
O(N)
FB Informatik
Prof. Dr. R.Nitsch
Bäume
O(log N)
O(1)
O(1)
O(log N)
Bei den logischen
Sequenzen (z.B.
sortierte Liste) sind
die Verhältnisse
genau umgekehrt!
Gesucht: Datenstruktur, die
die Vorteile von Liste und
Array vereint, also schnelles
Suchen, Einfügen und Löschen!
Lösung:
Bäume (Trees)!
10.01.2010
Nichtlineare Datenstruktur Baum
2
Datenstruktur Baum - Terminologie und Definitionen
• Bäume als wichtige Teilklasse von Graphen:
– Ein Baum ist ein einfacher, azyklischer,
zusammenhängender Graph (ungerichtete Kanten) Knoten
– bei n Knoten hat er genau n-1 Kanten
– Es gibt genau einen Pfad, der 2 Knoten verbindet
• Orientierter Baum (Wurzelbaum)
– Es gibt einen ausgezeichneten Knoten, den man
Wurzel nennt
– Die Wurzel bestimmt implizit die Orientierung
der Kanten (alle zur Wurzel hin oder von der
Wurzel weg gerichtet)
• Beispiel rechts:
b
– d ist die Wurzel
– d ist Elternknoten (Vorgänger) von b, k und e
a
– b ist Kind (Nachfolger) von d
– b, k und e haben denselben Elternknoten und sind
daher Geschwister
10.01.2010
Nichtlineare Datenstruktur Baum
FB Informatik
Prof. Dr. R.Nitsch
Kante
(ungerichteter) Baum
d
k
Wurzel (root)
e
c
g
f
h
3
Datenstruktur Baum - Terminologie und Definitionen
• Der Grad eines Knotens (die Ordnung)
ist durch die Anzahl seiner Kinder
bestimmt.
• Knoten ohne Kinder (Grad 0) heißen
Blatt. (a,c,k,f,h) Alle anderen Knoten
heißen innere Knoten (b,d,e,g)
• Jeder Knoten mit Ausnahme der Wurzel
hat genau einen Elternknoten.
• Der Grad (die Ordnung) eines Baumes
ergibt sich aus dem maximalen Grad
aller Knoten (manchmal auch auf
Englisch "fan-out" genannt)
• Die Nachfahren eines Knotens v sind
alle Nachfolger und Nachfolger von
Nachfolgern usw.
10.01.2010
FB Informatik
Prof. Dr. R.Nitsch
Grad=3
d
Wurzel (root)
Grad=1
k
b
a
e
c
Blatt
g
f
Grad=2
h
Wurzelbaum vom Grad 3
Nichtlineare Datenstruktur Baum
4
Datenstruktur Baum - Terminologie und Definitionen
• Die Ebene (oder Stufe) eines
Knotens v ist die Länge des
Pfades von der Wurzel zu v:
– Ebene der Wurzel = 0.
– Ebene eines Knotens v = 1 +
Ebene des Vorgängers von v.
• Die Höhe des Baumes ist die
maximale Ebene seiner Knoten.
• Das Gewicht eines Baumes ist die
Anzahl seiner Blätter.
FB Informatik
Prof. Dr. R.Nitsch
Ebene
0
d
k
b
a
1
e
c
g
f
2
i
3
Höhe = 3
10.01.2010
Nichtlineare Datenstruktur Baum
5
Binärer Baum und binärer Suchbaum (BST: Binary Search Tree)
• Wichtigster Sonderfall: Binärbaum =
Wurzelbaum mit Grad 2.
Definition eines binären Baums:
• Ein binärer Baum ist entweder ein leerer
Baum oder ein Knoten, der mit einem
Paar von binären Bäumen verbunden ist.
Diese beiden Bäume bezeichnet man als
linken und rechten Teilbaum.
• (Teil-)Bäume können auch leer sein.
Definition des binären Suchbaums (BST):
• Knoten enthalten einen eindeutigen
Schlüsselwert.
• Ein binärer Suchbaum ist ein binärer
Baum, bei dem die Knoten des linken
Teilbaums eines Knotens nur kleinere
Werte und die Knoten des rechten
Teilbaums eines Knotens nur größere
oder gleichgroße Werte als der Knoten
selbst besitzen (soweit es keine leeren
Teilbäume sind).
10.01.2010
d
Wurzel (root)
e
b
a
FB Informatik
Prof. Dr. R.Nitsch
c
g
Linker Teilbaum
f
Binärer Baum
und Binärer Suchbaum
Nichtlineare Datenstruktur Baum
h
Rechter Teilbaum
d
NULL
Links
Leere Teilbäume
(Manchmal auch als
externe Knoten bezeichnet)
6
Vollständiger und ausgeglichener binärer Baum
• Ein vollständiger Binärbaum enthält die für
seine Höhe maximale Anzahl von Knoten.
• Genauere Definition: Ein vollständiger
Binärbaum der Höhe h hat folgende
Eigenschaften:
– jeder Knoten der Ebene h ist ein Blatt
– jeder Knoten der Ebene i < h hat genau
2 Nachfolger
FB Informatik
Prof. Dr. R.Nitsch
0
d
1
f
b
a
c
e
h=2
g
vollständiger binärer Baum
• Definition: Bei einem ausgeglichenen
0
f
Binärbaum
– existiert ein k  0, so daß jedes Blatt
auf Ebene k oder k + 1 ist und jeder
Knoten auf einer kleineren Stufe als k
hat Grad 2.
• Definition: Ein entarteter Binärbaum liegt
vor, wenn jeder Elternknoten nur einen
Kindknoten hat
1
h
b
d
a
c
g
e
j
e
2
2
ausgeglichener binärer Baum
10.01.2010
Nichtlineare Datenstruktur Baum
7
Metriken und Eigenschaften von BST
• Maximale Anahl von Knoten
– auf Ebene i: 2i für i ≥ 0
– im vollständigen Binärbaum: 2h+1-1
• Pfadlänge eines Weges: Zahl der
Kanten auf dem Weg von der Wurzel
zum Knoten.
• Die max. Pfadlänge im BST ist
identisch mit seiner Höhe h.
– Die max. Pfadlänge vollständiger
oder ausgeglichener Binärbaume
mit n Knoten ist vorhersagbar:
max. Pfadlänge = h=int(log2n)
10.01.2010
FB Informatik
Prof. Dr. R.Nitsch
Ebene
0
d
0
b
1
e
1
1
a
c
g
2
2
2
Pfadlängen
Nichtlineare Datenstruktur Baum
2
f
i
3
3
3
Höhe = 3
8
Beispiel zum BST
FB Informatik
Prof. Dr. R.Nitsch
Eingefügt werden die Schlüsselwerte:
Suchen im BST (Fragen und Feststellungen)
80 47 96 20 15 60 25 42 73 23 33 37 28
• Wie sucht man nach einem Schlüsselwert (z.B.33)
– Eine erfolgreiche Suche endet am gesuchten Knoten
80
(Suchschlüssel). Der Suchaufwand (= Anzahl Vergleiche) ist
proportional zur Pfadlänge+1
47
96
• Woran erkennt man, ob ein Suchschlüssel nicht existierte (z.B. 79)?
20
60
– Eine vergebliche Suche (Suchfehler) endet immer am einem
leeren Teil-Baum (erkennbar am NULL-Link zum Nachfolger).
15
25
73
Leere Teilbäume sind im Beispiel durch Pseudo- oder externe
Knoten (rechteckige) dargestellt. Im Gegenzug bezeichnet man
23
42
alle ursprünglichen Knoten (runde) als interne Knoten.
33
– Aufwand wie bei erfolgloser Suche nach externem Knoten:
Pfadlänge zum externen Knoten+1
28
37
Einfügen im BST (Fragen und Feststellungen)
• Die Einfügereihenfolge ist nicht rekonstruierbar!
• Erfolgreiches Einfügen (keine Duplikate) beginnt wie eine
erfolglose Suche. Am Endpunkt der Suche, wird der externe Knoten
(NULL-Link) durch den einzufügenden Knoten ersetzt.
• Aufwand wie bei erfolgloser Suche nach externem Knoten:
Pfadlänge zum externen Knoten+1
10.01.2010
Nichtlineare Datenstruktur Baum
9
Metriken und Eigenschaften von BST
• Mittlerer Aufwand beim Suchen und
Einfügen sind proportional zur
mittleren Pfadlänge
• Die mittlere Pfadlänge für den
Beispiel-BST ist rechts dargestellt
• Allgemeine Betrachtung
– Beim ausgeglichenen BST gilt:
max. Pfadlänge =
Baumhöhe = O(log2n)
 Zeitkomplexität für Suchen
und Einfügen im BST O(log2n)
– Beim entarteten BST (worst-case)
 Zeitkomplexität für Suchen
und Einfügen im BST O(n)
FB Informatik
Prof. Dr. R.Nitsch
Pfadlänge L
NI L•NI
0
1 0
0
0
1
2 2
0
0
2
2 4
2
4
3
3 9
1
3
4
2 8
4 16
5
1 5
3 15
6
2 12
0
7
0 0
4 28
13 40
14 66
Ergänzendes Beispiel: n = 1 000 000
mittl. Anzahl Vergleiche bei Suche
in verketteter Liste:
in BST:
10.01.2010
NE L•NE
Nichtlineare Datenstruktur Baum
0
N=13 Knoten
80
47
20
15
96
60
25
23
73
42
33
28
37
BST verkettete
Liste
Mittlere Pfadlänge
wI = 40/13 = 3,1
wE = 66/14 = 4,7
7,5
14
10
Leistungsmerkmale von BST
FB Informatik
Prof. Dr. R.Nitsch
• Der schnellste Suchalgorithmus, die "Binäre Suche" benötigt ld(N) Vergleiche im
Mittel. Sie setzt (sortierte) physikalische Sequenzen voraus, die Einfüge- und
Löschoperationen aber nur mit O(N) zulassen .
• Die Suche in einem aus zufälligen Werten aufgebauten BST (der BST ist dann nicht
optimal ausgeglichen) benötigt im Mittel 1,39•ld(N) Vergleiche also O( ld N )
• Die Suche im BST ist damit nur um etwa 40% aufwändiger als die binäre Suche. Der
Aufwand für die Einfüge- und Löschoperationen ist unabhängig von der Knotenanzahl
O(1) (wie bei logischer linearer Sequenz).
Beispiel eines Binären Suchbaums:
Enstanden durch Einfügen von etwa 200 zufälligen Werten in einen anfangs leeren Baum.
Eine Suche benötigt im Durchschnitt 10 Vergleiche, nie mehr als 12 Vergleiche. In einer
200-elementigen Liste wird man im Mittel erst nach 100 Vergleichen fündig!
10.01.2010
Nichtlineare Datenstruktur Baum
11
Verständnisfragen
FB Informatik
Prof. Dr. R.Nitsch
• Um welchen Baumtyp handelt es sich?
K
•
•
•
•
•
•
Wieviele Knoten?
Wieviele Blätter?
Welche Höhe?
Vollständig?
Ausgeglichen?
Entartet?
10.01.2010
F
C
A
Nichtlineare Datenstruktur Baum
L
H
D
M
S
O
X
Y
U
12
Interne Darstellung eines Binären Suchbaums
FB Informatik
Prof. Dr. R.Nitsch
• In Programmiersprachen mit Pointern bzw.
Referenzen (C, C++, Java, …): Knotenobjekte (z.B.
Typ Node) enthalten
– Schlüsselwert und ggf zusätzliche assoziierte
Daten
– 2 Pointer/Referenzen auf die Nachfolger
– ggf zusätzlich 1 Pointer auf den Vorgänger
d
e
b
a
Wurzel (root)
c
g
f
h
Binärer Suchbaum
parent
struct Node {
Node( const K& t )
:key(t),left(0),right(0),parent(0) { }
~Node() { }
Node *left, *right, *parent;
K key;
parent
key
};
key K
left right
Datenstruktur in
struct Node
parent
K
left right
10.01.2010
Nichtlineare Datenstruktur Baum
key
K
left right
13
Implementierung eines BST Containers: class Tree
FB Informatik
Prof. Dr. R.Nitsch
typedef Any K;
struct Node
root
class Tree { // Implementation erlaubt keine Duplikate
lmost
parent
struct Node { /* siehe oben (eingeschachtelt) */ }
10
Node *root, *lmost; long size;
key K
size 5
public:
kleinstes Element im Baum
8
20
left right
typedef Node* link;
typedef long size_type;
15 30
typedef K key_type;
typedef K value_type;
Anforderungen an die Element-Klasse K
Tree() : root(0), lmost(0), size(0) { }
•Standard-Konstruktor
~Tree() { clear(); }
•Copy-Konstruktor
void clear();
class _Iter {
•bool K::operator<(const K&) const
link p;
•ostream& operator<<(ostream&,const K&);
public:
friend class Tree;
_Iter( link ptr=0 ) : p(ptr) { }
K& operator* () const { return p->key; }
bool operator==( const _Iter it ) const { return p == it.p; }
bool operator!=( const _Iter it ) const { return p != it.p; } class Tree implementiert
ein Wörterbuch(dictionary)
_Iter& operator++();
}; // END class _Iter
typedef _Iter iterator;
Basisoperationen im Baum
friend class _Iter;
• Suchen
_Iter find( const K& t ) const { return findR(t, root ); }
std::pair<_Iter,bool> insert(const K& t){return insertR(t,root);} • Einfügen
• Entfernen
size_type erase( const K& t ) { return eraseR(t, root ); }
(=Wörterbuchoperationen)
14
Nichtlineare
Datenstruktur
Baum
/*10.01.2010
Deklaration der Hilfsfunktionen findR, insertR,
eraseR*/
};
Suchen im BST
Rekursiver Algorithmus
für Suche im Baum
• Wenn der Teilbaum leer ist, liegt ein
Suchfehler vor.
• Wenn der Suchschlüssel t
– kleiner als der Wurzel-Schlüssel ist,
suche im linken Teilbaum
– größer als der Wurzel-Schlüssel ist,
suche im rechten Teilbaum
– den Suchschlüssel enthält, liegt ein
Suchtreffer vor.
FB Informatik
Prof. Dr. R.Nitsch
_Iter Tree::find(const K& t) { return findR(t,root);}
_Iter Tree::findR( const K& t, link r ) const
{
if( r==0 ) return end(); // Base case: Suchfehler
if( t < r->key ) return findR( t, r->left );
else if( r->key < t ) return findR( t, r->right );
else // Base case: r->key == t ; Suchtreffer!
return _Iter(r);
}
Wichtig: Algorithmus benötigt ausschliesslich K::operator<.
Dieser muss nicht die Objekte vom Typ K als Ganzes
vergleichen, sondern lediglich ihre Schlüsselwerte.
10.01.2010
Nichtlineare Datenstruktur Baum
10
8
20
15
30
15
Einfügen in einen BST
50, 40, 43
10, 8, 20, 10, 30
FB Informatik
Prof. Dr. R.Nitsch
• Die Operation "Einfügen" hängt neue Knoten
50
10
immer als Blatt an.
40
– (a) linker Teilbaum hat nur kleinere
8
20
Schlüsselwerte als die Wurzel
43
10 30
– (b) rechter Teilbaum hat nur größere oder
gleiche Schlüsselwerte als die Wurzel
(a)
• 2 Schritte:
Duplikat! (b)
– Suchen der Einfügestelle
Tree::iterator Tree::insert( const K& t ) { return insertR( t, root ); }
– Einketten (außer Duplikate)
pair<Tree::iterator,bool> Tree::insertR(const K& t,link& r) {
// Füge Objekt t in subtree mit
if( r==NULL ) {
// Wenn der Subtree leer ist, wird t als Wurzelknoten eingefügt // Wurzel r ein (rekursiv)
r = new Node(t); // Fügt eine Kopie des Objekts t ein Hier ist wichtig, dass der link als Referenz
++size;
übergeben wurde: link& == Node*&
if( lmost == NULL ) lmost = r;
// Prüfen, ob neuer kleinster
parent
else if( r->key < lmost->key ) lmost = r;
// Wert vorliegt
return pair<_Iter,bool>(_Iter(r),true);
key K
Node*&
}
// Gibt Iterator auf eingefügten Knoten zurück
left right
pair<_Iter,bool> success;
Node*
// Objekt t in linken subtree einfügen, wenn Schlüssel < Wurzel
if( t < r->key )
{ success = insertR( t, r->left ); r->left->parent = r;
return success; }
// Objekt in rechten subtree einfügen, wenn Schlüssel ≥ Wurzel
parent
else if( r->key < t )
key K
{ success = insertR( t, r->right ); r->right->parent = r;
left right
return success; }
else return pair<_Iter,bool> (_Iter(r),false);
} 10.01.2010
16
Nichtlineare Datenstruktur Baum
Traversieren eines Binärbaumes
FB Informatik
Prof. Dr. R.Nitsch
• Problem: Man möchte alle Daten, die in einem Binärbaum gespeichert sind, besuchen zum
Zwecke der Verarbeitung der assoziierten Daten; z.B. als Tabelle ausgeben oder ihre Anzahl
oder ihre Summe berechnen
• Mit Traversieren eines Binärbaumes wird das systematische Besuchen all seiner Knoten
bezeichnet
• Enthält ein Baum N Knoten, so gibt es N! (Fakultät) verschiedene Reihenfolgen, in welchen man
die Knoten eines Binärbaumes besuchen kann
• Beispiel: Die Knoten A, B, C eines Baumes können in 3! = 6 verschiedenen Reihenfolgen
traversiert werden
ABC
BAC
CAB
ACB
BCA
CBA
• Die drei wichtigsten Traversierungsvarianten eines Baums werden mit Preoder, Inorder- und
Postorder-Reihenfolge bezeichnet und lassen sich rekursiv definieren:
– Preorder:
Besuch der Wurzel, danach den linken und danach den rechten Teilbaum. Dort
wiederholt sich die Besuchsreihenfolge
– Inorder:
Besuch des linken Teilbaums in Inorder-Reihenfolge, danach Besuch der
Wurzel, danach Besuch des rechten Teilbaums in Inorder-Reihenfolge
– Postorder: Besuch des linken Teilbaums in Postorder-Reihenfolge, danach Besuch des
rechten Teilbaums in Postorder-Reihenfolge, danach Besuch der Wurzel
10.01.2010
Nichtlineare Datenstruktur Baum
17
Traversieren von BST
FB Informatik
Prof. Dr. R.Nitsch
void Tree::toStreamRin( ostream& os, const link subtree ) const {
// visits all keys in sorted order (recursive inorder traversal)
if( subtree==NULL ) ; // nothing to do
else {
// print left subtree
toStreamRin( os, subtree->left );
// print key
os << subtree->key << ' '; // ostream& operator<<(ostream& os,const K&)
// erforderlich
// print right subtree
toStreamRin( os, subtree->right );
}
}
g
4
c
a
1
2
k
f
j
3
5
6
m
7
in-order Reihenfolge
g
1
void Tree::toStreamRpre( ostream& os, const link subtree ) const {
c
k
// visits all keys in pre-order sequence
2
5
if( subtree==NULL ) ; // nothing to do
a
f
j
m
else {
3
4
6
7
// print key
os << subtree->key << ' ';
pre-order Reihenfolge
// print left subtree
toStreamRpre( os, subtree->left );
Anwendungen
// print right subtree
•in-order Traversieren sortiert die Elemente
toStreamRpre( os, subtree->right );
}
•pre-order Ttraversieren wird z.B. im Copy}
Konstruktor und operator= verwendet (erzeugt
exakte Baum-Kopie)
10.01.2010
Nichtlineare Datenstruktur Baum
18
Traversieren von BST
FB Informatik
Prof. Dr. R.Nitsch
void Tree::toStreamRpost( ostream& os, const link subtree ) const {
//visits all keys in post-order sequence recursive
if( subtree == NULL ) ; // nothing to do
else {
// print left subtree
toStreamRpost( os, subtree->left );
// print right subtree
toStreamRpost( os, subtree->right );
// print key
Anwendungen
os << subtree->key << ' ';
Post-order Reihenfolge wird
}
return;
z.B.beim Löschen eines Baums
}
angewendet
Tree::enum Order { preOrder,
7
c
a
1
3
k
f
j
2
4
6
post-order Reihenfolge
=inOrder ) const {
root ); break;
root ); break;
root ); break;
Nichtlineare Datenstruktur Baum
m
5
inOrder, postOrder };
void toStream( ostream& os, Order order
if( !empty() )
switch(order) {
case inOrder:
toStreamRin ( os,
case preOrder:
toStreamRpre ( os,
case postOrder: toStreamRpost( os,
};
}
10.01.2010
g
19
Klasse Tree: Copy-Konstruktor und Zuweisung
Tree( const Tree& t ) {
root = NULL; lmost=NULL; size=0;
copyR( t.root );
}
FB Informatik
Prof. Dr. R.Nitsch
Copy-Konstruktor
g
c
2
1
k
5
a
f
j
m
void Tree::copyR( link subtree ) {
3
4
6
7
// Rekursiver Algorithmus (Traversieren in preorder Reihenfolge)
// Erstellt eine exakte Kopie des Teilbaums
pre-order Reihenfolge
if( subtree == NULL )
; // Nichts mehr zu kopieren
else {
this->insert( subtree->key );
// Kopie der Wurzel in Zielbaum (this) einfügen
this->copyR ( subtree->left ); // linken Teilbaum kopieren
this->copyR ( subtree->right ); // rechten Teilbaum kopieren
}
}
Anwendung im Zuweisungsoperator
Pre-order
Reihenfolge
Tree& Tree::operator=( const Tree& t ) {
if( this == &t ) return *this; // self assignment
this->clear();
this->copyR( t.root );
}
10.01.2010
Nichtlineare Datenstruktur Baum
20
Vollständiges Löschen eines Baums oder Teilbaums
FB Informatik
Prof. Dr. R.Nitsch
void Tree::clear() { clearR( root ); lmost = 0; }
void Tree::clearR( link& subtree ) {
// deletes subtree with root subtree using post-order traversal
if( subtree!=NULL ) {
clearR( subtree->left ); //erase left subtree
post-order
clearR( subtree->right ); //erase right subtree
Reihenfolge
delete subtree ;
//erase root
subtree = NULL;
//and replace with external node
--size;
}
return;
}
g
7
c
a
1
3
k
f
j
2
4
6
m
5
post-order Reihenfolge
Anwendung im Destruktor
~Tree() { clear(); }
10.01.2010
Nichtlineare Datenstruktur Baum
21
Rekursive Berechnung von Baummetriken
FB Informatik
Prof. Dr. R.Nitsch
• Rekursive Ermittlung der Knotenanzahl (Alternative zur Zählung mittels speziellem
Attribut):
private: long Tree::countR( link subtree ) {
if ( subtree==NULL ) return 0; // Base case: "Da ist kein weiterer Knoten mehr!"
return
countR( subtree->left ) // Zähle Knoten im linken Teilbaum
+ countR( subtree->right ) // Zähle Knoten im rechten Teilbaum und addiere hinzu
+ 1; // Das ist für den Wurzelknoten
root
Erklärung?
Besuchsreihenfolge?
}
10
public: long Tree::count() {
return countR( root );
}
8
• Rekursive Ermittlung der (Teil-)Baum-Höhe:
20
10
30
private: long heightR( link subtree ) {
if ( subtree==NULL ) return 0;
Externer Knoten: Höhe=0 -> base case
long hl = heightR( subtree->left );
Bestimme Höhe des rechten Teilbaums
long hr = heightR( subtree->right ); Bestimme Höhe des linken Teilbaums
if( hl>hr ) return hl+1; else return hr+1;
Wurzelknoten liegt 1 Ebene höher
als seine Teilbäume!
}
public: long getHeight() {
return heightR( root );
}
10.01.2010
Nichtlineare Datenstruktur Baum
22
Ausgabe der Baumstruktur als Textsequenz
FB Informatik
Prof. Dr. R.Nitsch
• Anwendung: Unterstützung beim Debuggen von Algorithmen mit Bäumen
private: void Tree::toStreamR( ostream& os, link r, int height ) {
if( r==NULL ) {
os << string(height, ' ' ) << '*' << endl; // Externer Knoten -> base case
return;
}
toStreamR( os, r->right, height+1 );
os << string(height, ' ' ) << r->info << endl;
inorder traversieren
toStream( os, r->left, height+1 );
}
public: void toStream( ostream& os ) { toStreamR( os, root, 0 ); }
Beispiele: Einfügesequenz: 1, 2, 3, 4
Ausgabe:
*
4
*
3
Einfügesequenz: 3, 1, 2, 4
Ausgabe:
*
2
*
1
*
10.01.2010
In welcher Reihenfolge müssen die Knoten
zur Ausgabe besucht werden?
*
4
*
3
*
2
*
1
*
Hausaufgabe: Modifizieren Sie den
Quellcode für den Fall, dass die Feldbreite für
info w Zeichen (w>1) beträgt.
Nichtlineare Datenstruktur Baum
23
Löschen von einzelnen Elementen im Baum
Fallunterscheidung
1. Zu löschendes Element D ist ein Blatt:
Entferne Blatt
FB Informatik
Prof. Dr. R.Nitsch
vorher
nachher
D
vorher
nachher
2. Zu löschendes Element D hat genau einen
Nachfolger N: Ersetze D durch N
N
D
N
3. Zu löschendes Element D hat 2 Nachfolger
DL und DR:
Methode 2: Ersetze Schlüsselwert im Element D
Methode 1: Ersetze D durch den linken
durch eine Kopie des größten Schlüsselwerts im
Nachfolger DL und mache den rechten
linken Teilbaum von D (Element R) und lösche R.
Nachfolger DR zum rechten Nachfolger des
vorher
nachher
größten Elements R des linken Teilbaums von D
vorher
nachher
D
R
D
DL
DR
DL
R
10.01.2010
DR
DL
R
R
DR
RL
Nichtlineare Datenstruktur Baum
DL
DR
RL
Vorteil: Geringere Änderungen
in Baumstruktur
24
Löschen von einzelnen Elementen im Baum
FB Informatik
Prof. Dr. R.Nitsch
// Schlüsselwert k suchen und entfernen
Tree::size_type Tree::erase( const K& k ) {
// Nichts zu tun; nichts gelöscht
if( empty() ) return 0;
size_type numberOfErasedNodes = removeR( k, root );
return numberOfErasedNodes;
// nur 0 und 1 sind mögliche Werte (keine Duplikate im Tree!)
}
Tree::size_type Tree::removeR( const K& k, link& subtree ) { // class Tree erlaubt keine Duplikate
// search key
if( subtree==NULL ) return 0; // Base case 1: Key k nicht gefunden; nichts gelöscht
if( k < subtree->key )
// key k aus linkem Subtree entfernen
return removeR( k, subtree->left );
// Beachte: Ein Suchwert ist gefunden, wenn
// !(searched<current)&& !(current<searched)
else if( subtree->key < k ) // key k aus rechtem Subtree entfernen
// d.h. Aquivalenzoperator (key_type::operator==)
return removeR( k, subtree->right );
// wird nicht benutzt und muss daher auch nicht
// definiert sein!
else
// Base case 2: Key an Wurzel des Subtree gefunden.
{
link d = subtree;
// Zeiger auf gefundenes Lösch-Element zur besseren Lesbarkeit
if( d->left==NULL && d->right==NULL ) // Fall 1: Zu löschendes Element ist ein Blatt (keine Nachfolger)
{
vorher
nachher
subtree = NULL;// Schlüssel ausketten
delete d; --size;
// Schlüssel löschen
D
return 1;
}
10.01.2010
Nichtlineare Datenstruktur Baum
25
Löschen von einzelnen Elementen im Baum
if( d->left!=NULL && d->right!=NULL ) {
return remove2child( d->left, d );
}
if( d->left==NULL) {
subtree = d->right;
subtree->parent = d->parent;
}
else {
subtree = d->left;
subtree->parent = d->parent;
}
delete d; --size; return 1;
} //else
FB Informatik
Prof. Dr. R.Nitsch
// Fall 3: 2 Nachfolger
Zunächst wird Fall
2 erklärt (einfacher)
// Maximum aus linkem Subtree
// ins ple-Element kopieren und löschen
// Fall 2: Nur rechter Nachfolger
// Schlüssel ausketten
// Fall 2: Nur linker Nachfolger
// Schlüssel ausketten
vorher
nachher
D
sroot
nachher
vorher
D
sroot
// Schlüssel löschen
}
size_type remove2child( link& subtree, link& d ) {
if( subtree->right!=NULL ) // Solange rechts weitersuchen, bis Maximum gefunden
return remove2child( subtree->right, d );
vorher
else {
// subtree->right != 0; Maximum R gefunden
ple
link r = subtree;
// Verbessert Lesbarkeit
l
d->key = r->key;
// r nach d kopieren
sroot
if(r->left!=NULL)
// wenn r kein Blatt…
r->left->parent = r->parent;
// … rl an Stelle von r
s
LR
LL
// einketten
subtree = r->left;
r
delete r; --size; return 1;
// r löschen
rl
}
10.01.2010
Nichtlineare Datenstruktur Baum
}
nachher
r
LL
s
LR
rl
26
Rotationen und ihre Anwendungen
FB Informatik
Prof. Dr. R.Nitsch
• Eine Rotation vertauscht die Rolle
vorher
B
einer Wurzel (S bzw. B) mit einem
ihrer unmittelbaren Nachfolger (E).
• Eine Rechts-(Links-)Rotation wirkt auf
die Wurzel und den linken (rechten)rotierendes Element E
Nachfolger
• Durch eine Rechts-Rotation wird das
el
rotierende Element und sein linker
Teilbaum um eine Ebene angehoben
während die Wurzel (S) und ihr
rechter Teilbaum um eine Ebene
vorher
abgesenkt wird.
Entsprechendes gilt für die LinksB
Rotation.
• Anwendungen
bl
– Einfügen an der Wurzel
– Löschen von Elementen
el
Ausgeglichene BST
Wurzel
nachher
B
S
E
S
sr
el
er
er
sr
E≤ Schlüsselwerte <S
nachher
S
S
E
E
B
er
er
bl
el
Rechtsrotation (oben) und Linksrotation (unten)
10.01.2010
Nichtlineare Datenstruktur Baum
27
Einfügen an der Wurzel in einem BST
A
• Standardimplementierung: Zuletzt eingefügte
Schlüssel haben immer einen langen Suchpfad:
Sie bilden ein Blatt im Baum.
• Wenn Anwendungen auf neu eingefügte Elemente
öfter zugreifen als auf andere, sollte man besser
an der Baumwurzel einfügen.
• Für die dabei auftretenden Probleme gibt es eine
Lösung: Einfügen als Blatt und rekursive
Anwendung von Rotationen:
1. G als Blatt einfügen
2. Rechts-Rotation
3. Links-Rotation
4. Rechts-Rotation
5. Links-Rotation
G
A
Probleme beim
Einfügen an
L
der Wurzel
A
C
Vorher
10.01.2010
R
L
C
R
Fehler!
FB Informatik
Prof. Dr. R.Nitsch
A
S
E
G
X
C
E
R
1
S
C
G
R
X
4
A
G
S
E
C
A
S
X
E
G
2
R
X
C
R
5
A
S
G
E
3
X
R
C
Wenn man auch die Operation "Suchen" so ändert,
daß jeder Suchtreffer durch rekursive Rotationen
an die Wurzel befördert wird, erhält man ein
selbstorganisierendes Suchverfahren, das häufig
angesprochene Elemente an der Wurzel sammelt.
Nachher
Nichtlineare Datenstruktur Baum
28
Implementation der Rotationstransformationen
FB Informatik
Prof. Dr. R.Nitsch
void Tree::rotR( link& s ) {
vorher
b
//
Linken
Subtree
mit
Wurzel
e
von
s
abkoppeln
link e = s->left;
if(e==NULL) return; // Linker Subtree von s fehlt; nichts zu rotieren.
s
if(e->right!=NULL) { // Wenn rechter Subtree von e existiert
e
// Rechter Subtree von e wird neuer linker Subtree von s
s->left = e->right;
sr
// Rückwärtslink von er nach s
e->right->parent = s;
// Nichts anzubinden -> ext. Knoten
} else s->left = NULL;
el
er
e->right = s;
// s als rechten Subtree an e anbinden
e->parent = s->parent;
// Rückwärtslink von e zu b
s->parent = e;
// Rückwärtslink von s zu e
s = e;
// e an b anbinden
Rotation = Rollentausch
}
void Tree::rotL( link& b ) {
link e = b->right;
if(e==NULL) return;
if(e->left!=NULL) {
b->right = e->left;
b->right->parent = b;
} else b->right = NULL;
e->left = b;
e->parent = b->parent;
b->parent = e;
b = e;
}
10.01.2010
e
s
el
er
Nichtlineare Datenstruktur Baum
vorher
sr
parent
key K
left right
zwischen Wurzel und
einem ihrer Nachfolger
// Rechten Subtree mit Wurzel e von b abkoppeln
// Rechter Teilbaum von s fehlt; nicht zu rotieren.
// Wenn rechter Subtree von e existiert
// Linker Subtree von e wird neuer rechter Subtree von b
// Rückwärtslink von el nach b
// Nichts anzubinden -> ext. Knoten
// b als linken Subtree an e anbinden
// Rückwärtslink von b nach s
bl
// Rückwärtslink von b nach e
// e an s anbinden
nachher
b
nachher
s
b
s
e
e
b
er
el
er
bl
el
29
Algorithmus zum Einfügen an der Wurzel
FB Informatik
Prof. Dr. R.Nitsch
• Im vorigen Beispiel wurde die Einfügestelle für G am Ende des Suchpfades A-T-E-R gefunden.
• Entlang dieses Suchpfades wird das Einfügeelement durch Rotationen zur Wurzel gebracht.
• Bei jedem Rotationsvorgang ist die Richtung der Rotation des Vorgängerelements gegeben durch
das Kindschaftsverhältnis zum Vorgängerknoten: Einfügeelement ist
– linkes Kind
-> Rechtsrotation des Vorgängerknotens
– rechtes Kind -> Linksrotation des Vorgängerknotens
• Das Einfügeelement wird zur Wurzel, wenn jeder Knoten des Suchpfades einmal rotiert wurde.
A
void insertRootR( const Key& k, link& s ) {
T
// Einfügeposition erreicht; Ende des Suchpfades (base case)
if( s==0 ) {
E
s = new Node( k ); // Einfügen als Blatt
C
R
return;
1
G
}
// beim linken Nachfolger weiter suchen
if( k < s->key ) { insertRootR( k, s->left ); rotR( s ); }
else { insertRootR( k, s->right ); rotL( s ); } // danach Rechtsrotation des Knotens
}
// beim rechten Nachfolger weiter suchen // danach Linksrotation des Knotens
public: _Iter insertRoot( const Key& k ) {
insertRootR( k, root );
return begin();
}
Baumstruktur *
5
vor dem
*
Einfügen:
4
*
Baumstruktur
nach dem
Einfügen des
Elements k=2:
*
5
*
4
*
3
3
*
1
X
*
2
*
*
1
10.01.2010
Nichtlineare Datenstruktur Baum
*
30
Nocheinmal: Löschen von einzelnen Elementen im Baum (Methode 3)
• Bilde aus linkem und rechtem Teilbaum des Löschelements
D einen Verbundbaum (Methode joinLR). Dieser wird an
Stelle des Löschelements mit dessen Vorgänger verbunden.
• Suche dazu kleinsten Schlüsselwert M im rechten
Teilbaum und mache diesen zur Wurzel des rechten
Teilbaums (wird von min2root geleistet) und
• Mache den linken Teilbaum zum linken Nachfolger der
neuen Wurzel M.
vorher
FB Informatik
Prof. Dr. R.Nitsch
nachher
D
DL
DR
M
M
DL
DR'
Implementierung
public: size_type Tree::erase( const K& k ) {
return eraseR( k, root );
}
Rekursive Löschmethode eraseR sucht Schlüsselwert k im (Teil)Baum mit Wurzel r und entfernt dieses Element.
private: size_type Tree::eraseR( const K& k, link& r ) {
if(r==NULL) return 0; // base case: externer Knoten erreicht; Löschschlüssel nicht gefunden!
if( k<r->key ) return eraseR( k, r->left );
// im linken Teilbaum weiter suchen
else if( r->key<k ) return eraseR( k, r->right );
// im rechten Teilbaum weiter suchen
else {
// k bei r gefunden
link d = r;
// Verbindung zu Löschknoten in d temporär halten
r = joinLR( d->left, d->right );
// joinLR verbindet rechten und linken Teilbaum und gibt Zeiger
if(r!=NULL) r->parent = d->parent;
// auf neue Wurzel zurück. Diese wird an den Vorgänger des
delete d; --size; return 1;
// Löschknotens angekoppelt
}
}
10.01.2010
Nichtlineare Datenstruktur Baum
31
Nocheinmal: Löschen von einzelnen Elementen im Baum (Methode 3)
FB Informatik
Prof. Dr. R.Nitsch
link joinLR( link a, link b ){ // Teilbäume a und b verbinden; Zeiger auf Verbundbaum-Wurzel zurück geben
if( b==NULL ) return a;
// rechter Teilbaum ist leer; a ist schon Verbundbaum-Wurzel
min2root( b );
// Minimum in rechtem Teilbaum suchen und zur Wurzel machen
b->left = a;
// linken Teilbaum an Wurzel b anhängen
if( a!=NULL ) a->parent = b; // Wenn linker Teilbaum nicht leer ist
return b;
// Neue Verbundbaum-Wurzel zurückgeben
}
void min2root( link& r )
{
if( r->left != NULL ) {
min2root( r->left );
rotR( r );
}
assert( r->left==NULL );
return;
}
// Macht kleinsten Schlüsselwert eines (Teil)Baums zur Wurzel
// In die linken Teilbäume hinabsteigen bis Blatt (=Nachfolger des Löschelements)
// erreicht ist. Danach dieses Blatt mittels Rechtsrotationen zur Wurzel machen
// Nachbedingung: Der linke Teilbaum zur Wurzel muss leer sein
vorher
nachher
D
DL
10.01.2010
Nichtlineare Datenstruktur Baum
DR
M
M
DL
DR'
32
Bewertung von BST
FB Informatik
Prof. Dr. R.Nitsch
• Vorteile
– Einfüge- und Löschoperationen von O(1)
– Suchoperationen bestenfalls von O(log(N))
• Nachteile:
– Hoher Speicheraufwand für die Verbindungen
– Die fehlende Ausgeglichenheit von BST läßt keine Leistungsgarantien zu:
• Bereits sortierte (auch umgekehrt sortierte) Dateien,
• Dateien mit vielen mehrfachen Schlüsseln,
• Dateien mit abwechselnd großen und kleinen Schlüsseln
können zu entarteten BST und damit quadratischen Konstruktionszeiten O(N2)
und linearen Suchzeiten O(N) führen
– Abhilfe: Algorithmen, die einen BST explizit ganz neu ausgleichen (z.B. nach fester
Anzahl von Einfügen- und Löschoperationen.
10.01.2010
Nichtlineare Datenstruktur Baum
33
Algorithmus zum Ausgleichen eines BST
• Idee:
– Median im BST suchen und an die Wurzel bringen: Linker
und rechter Teilbaum enthalten N/2 bzw. N/2-1 Knoten.
– Gleiches mit den Teilbäumen rekursiv wiederholen bis
Teilbäume leer sind.  balanceR
• benötigt wird dazu:
– partR: Ein Algorithmus, der das k-kleinste Element
auswählt und danach an die Wurzel bringt; für k=N/2 ist
dies der Median
FB Informatik
Prof. Dr. R.Nitsch
1
Vorher
2
3
4
4
2
1
5
6
3
5
6
7
7
Nachher
void Tree::balanceR( link& subtree )
// base case: externer Knoten oder Blatt (Nichts zu partitionieren)
{
if(subtree==NULL || countR(subtree)==1) return;
partR(subtree, countR(subtree )/2 ); // Median bestimmen und zur Wurzel rotieren
balanceR( subtree->left );
// Gleiches für linken und rechten Teilbaum wiederholen
balanceR( subtree->right );
}
void partR( link& subtree, int k ) {
int n = countR(subtree->left);
// n: Knotenanzahl im linken Teilbaum
if(k<n) {
// k-kleinstes Element muß im linken Teilbaum sein
partR(subtree->left,k); rotR(subtree);
} else if(n<k) {
// k-kleinstes Element muß im rechten Teilbaum sein.
partR(subtree->right,k-n-1); rotL(subtree);
// Dort ist es aber das (k-n-1)-kleinste!
} else ;
return;
34
Nichtlineare Datenstruktur Baum
} 10.01.2010
Rot-Schwarz-Bäume
FB Informatik
Prof. Dr. R.Nitsch
• Das gelegentliche vollständige Ausgleichen (z.B nach einer bestimmten Anzahl von
Einfüge/Lösch-Operationen) verbessert die Leistungsfähigkeit eines BST nur begrenzt.
Insbesondere können immer noch keine Leistungsgarantien gegeben werden, weil die
Höhe log(N) nicht immer garantiert ist.
• Sortierte höhen-balancierte Schlüsselbäume bieten hier mehr: Die Operationen Einfügen
und Suchen sind in höhen-balancierten Bäumen am schnellsten. Allerdings steigt bei
diesen der algorithmische Aufwand:
– Unzulässige Höhenunterschiede zweier Teilbäume müssen erkannt und mittels
Rotationen beseitigt werden.
• Ein bekannter Vertreter dieser Spezies ist der Rot-Schwarz-Baum (Red-Black-Tree). Die
assoziativen STL-Container gehören auch dazu.
• RBT verwalten ein zusätzliches Farbattribut pro Knoten
• RBT mit N internen Knoten garantieren:
– maximale Pfadlänge <= 2 * minimale Pfadlänge
– Baumhöhe <= 2 log (N+1)
– Suchen, Auswählen -> O(log N) garantiert
10.01.2010
Nichtlineare Datenstruktur Baum
35
Definition und weitere Eigenschaften
FB Informatik
Prof. Dr. R.Nitsch
Ein Rot-Schwarz-Baum (RBT) ist ein sortierter binärer Schlüsselbaum mit folgenden Eigenschaften
(Invarianten):
1. Jeder Knoten ist entweder rot oder schwarz (RB1)
2. Jeder externe Knoten ist schwarz (RB2)
3. Rote Knoten haben immer 2 schwarze Söhne (RB3)
4. Alle Pfade von externen Knoten zur Wurzel enthalten dieselbe Anzahl schwarzer Knoten (RB4)
Beispiel für einen RBT
K
F
C
A
S
H
D
B
O
L
X
class Tree {
enum Color { RED='r', BLACK='s'};
struct Node {
Node(const K& k, Color c=BLACK)
:key(k),left(0),
right(0),parent(0),
Black is nobly
color(c) { }
~Node() { }
U
Node *left, *right, *parent;
K key; Color color;
E
};
typedef Node* link;
// Rest wie gehabt
Externe Knoten sind alle schwarz und nicht dargestellt!
};
10.01.2010
Nichtlineare Datenstruktur Baum
36
Einfügen und Suchen in einem RBT
FB Informatik
Prof. Dr. R.Nitsch
• Algorithmische Konsequenzen:
– Das Suchen funktioniert wie beim BST-Tree -> Laufzeit O(h)=O(log N)
– Das Einfügen eines Knotens x erfolgt zunächst wie beim BST. Falls dabei Eigenschaft 3 oder 4
verloren geht, muss die Farbstruktur des Baumes repariert werden.
RB1: Jeder Knoten ist entweder rot oder schwarz (RB1)
RB2: Jeder externe Knoten ist schwarz (RB2)
RB3: Rote Knoten haben immer 2 schwarze Söhne (RB3)
RB4: Alle Pfade von externen Knoten zur Wurzel enthalten dieselbe Anzahl schwarzer Knoten
Einfügen eines Knotens x erfolgt zunächst immer als Blatt.
Dabei wird x immer rot eingefärbt. Grund: weniger Farb
korrekturen, weil
- Bei schwarzem x würde immer RB4 verletzt!
-Bei rotem x wird nur dann eine Regel (RB3) verletzt,
3
wenn der Vater von x auch rot ist (s. Beispiel).
Farben reparieren
6
o
g
6
12
Wenn das rote x auch einen roten Vater v hat,
x
unterscheidet man:
8
Fall 1: Onkel o von x (3) ist auch rot -> o und v schwarz
machen
Problem: Jeder Pfad über den Großvater g hat jetzt
Neu eingefügt!
einen zusätzlichen schwarzen Knoten.
Lösung: g rot machen
Konsequenz: RB3&4 können jetzt 2 Etagen höher wieder verletzt sein!
10.01.2010
Nichtlineare Datenstruktur Baum
v
3
g
o
12
8
x
37
v
Einfügen in einem RBT - Implementierung
FB Informatik
Prof. Dr. R.Nitsch
pair<Tree::_Iter,bool> Tree::insert_rb( const T& t ) {
if( root!=NULL ) assert( root->color == BLACK );
pair<_Iter,bool> success = insertR( t, root ); // insertR wird wiederverwendet
if (success.second==false) return success;
// Duplikat! Nichts eingefügt
// Akteure festlegen
link x = success.first.nodePtr; // Eingefügter ( standardmäßig scharzer) Knoten
link v = x->parent;
// Vater von x
if( v==NULL ) return success;
// x ist Baumwurzel; fertig
link g = v->parent;
// Grossvater von x
link o = NULL;
// Onkel von x; noch unbekannt
Color ocolor;
// Farbe des Onkels
x->color = RED;
// Eingefügt wird immer zunächst als roter Knoten
if( g==NULL ) return success;
// Vater ist Wurzelknoten und daher schwarz; x ist jetzt rot -> alles ok!
// Vater und Großvater existieren
10.01.2010
Nichtlineare Datenstruktur Baum
38
Einfügen in RBT - Implementierung
FB Informatik
Prof. Dr. R.Nitsch
// Vater und Großvater existieren
// RBT-Invarianten müssen geprüft werden
if( v->color==RED )
// Roter Vater (v) mit rotem Sohn (x) -> RB3 verletzt
{
while( x!=root && v->color==RED )
// Auf dem Weg zur Wurzel dürfen keine 2 roten Knoten
{ // Akteure neu bestimmen
// aufeinander folgen (RB3)
g = v->parent;
// g ist Großvater von x
if( v==g->left )
// Wenn Vater linker Sohn des Großvaters ist
{
o = g->right;
// dann ist Onkel von x der rechte Sohn des Großvaters
if(o==NULL) ocolor = BLACK;
// Ein nil-Onkel ist immer schwarz->Fall 2
else ocolor = o->color;
if( ocolor==RED )
// Fall 1: Roter Vater und roter Onkel
{
// Vater umfärben
v->color = BLACK;
// Onkel umfärben
o->color = BLACK;
// Großvater umfärben
g->color = RED;
// Hier ist jetzt alles erledigt // aber 2 Stufen höher jetzt alles nochmal
// Deshalb Akteure neu bestimmen
x = g;
// Fortsetzen als wäre soeben roter g eingefügt worden
v = x->parent;
// v ist Vater von x
} else //ocolor is BLACK
// Fall2: Roter Vater und schwarzer Onkel
10.01.2010
Nichtlineare Datenstruktur Baum
39
Einfügen in einem RBT
FB Informatik
Prof. Dr. R.Nitsch
RB1: Jeder Knoten ist entweder rot oder schwarz (RB1)
RB2: Jeder externe Knoten ist schwarz (RB2)
RB3: Rote Knoten haben immer 2 schwarze Söhne (RB3)
RB4: Alle Pfade von externen Knoten zur Wurzel enthalten dieselbe Anzahl schwarzer Knoten
Fall 2: Onkel o von x ist schwarz oder nil
-> Farben reparieren durch rotieren!
Hinweis: nil steht für "not in list" und steht für einen
nicht existierenden (=externen) Knoten.
Fall 2a: x ist linker Sohn seines Vaters v.
1. Vater v (5) schwarz färben -> RB4 verletzt
2. Großvater g wieder rot färben -> RB4 verletzt
3. R-Rotation um g (9) -> fertig, alles ok!
1.
9
5
2
v
x
2.
g
9
o
5
2
x
v
g
9
o
5
2
v
3.
g
o
5
2
9
v
x
Neu eingefügt!
10.01.2010
Nichtlineare Datenstruktur Baum
40
Einfügen in einem RBT
FB Informatik
Prof. Dr. R.Nitsch
RB1: Jeder Knoten ist entweder rot oder schwarz (RB1)
RB2: Jeder externe Knoten ist schwarz (RB2)
RB3: Rote Knoten haben immer 2 schwarze Söhne (RB3)
RB4: Alle Pfade von externen Knoten zur Wurzel enthalten dieselbe Anzahl schwarzer Knoten
Fall 2: Onkel o von x ist schwarz oder nil
-> Farben reparieren durch rotieren!
Hinweis: nil steht für "not in list" und steht für einen
nicht existierenden (=externen) Knoten.
Fall 2b: x ist rechter Sohn seines Vaters v.
1. L-Rotation um v (5) -> Fall 2a
2. Rest wie Fall 2a
Fall 2a
Fall 2a, 1. bis 3.
1.
9
5
v
g
9
g
7
o
v
x
5
7
9
g
v
7
x
5
Rollentausch!
x
Neu eingefügt!
10.01.2010
Nichtlineare Datenstruktur Baum
41
Einfügen in RBT - Implementierung
{
if( x==v->right ) {
rotL(g->left);
x = v;
v = x->parent;
}
v->color = BLACK;
g->color = RED;
FB Informatik
Prof. Dr. R.Nitsch
// Fall 2: Roter Vater und schwarzer Onkel
// Fall 2b: x ist rechter Sohn
// erst in Fall 2a umwandeln
// Rollentausch zwischen v und x
// Ab hier Fall 2a
// Vater umfärben
// Großvater auch
// Farben durch Rotation um g reparieren
// Von wem stammt der Großvater ab: Urgroßvater oder Gott/Allah (root) ?
// Großvater hat einen Urgroßvater dessen linker oder rechter Sohn er ist
if( g->parent )
g->parent->left==g ? rotR( g->parent->left ) : rotR( g->parent->right );
else
// Großvater ist Adam und hat keinen Vorgänger ausser root
rotR( root );
} // END ELSE ocolor is BLACK
}
else
{
...
}
}
// END if( v==g->left ) Vater ist linker Sohn des Großvaters
// Vater ist rechter Sohn des Großvaters
// Das ganze nochmal, aber rechts und links vertauschen, auch bei den Rotationen
// END while Schleife terminiert
}
else ; //END IF(v->color==RED)
// Schwarzer Vater mit rotem Sohn. Keine RBT-Regel verletzt
root->color = BLACK; Die Wurzel ist immer schwarz
return success;
}
10.01.2010
Nichtlineare Datenstruktur Baum
42
Algorithmus zum Testen der RBT-Eigenschaften
FB Informatik
Prof. Dr. R.Nitsch
bool isRBTree()
{
isRBT = true;
blackHeight = -1; // Anzahl schwarzer Knoten auf dem Weg von der Wurzel zu einem Blatt (Attribut von Tree)
if (root!=NULL) return isRBTreeR( root, 0 ); else return true ;
}
bool isRBTreeR( link r, int bheight ) {
// Schwarzhöhe für diese Aufrufebene aktualisieren
if ( r->color==BLACK ) ++bheight;
if(r->left==NULL && r->right==NULL) { // Blatt erreicht? (base case)
if( blackHeight==-1 ) {
// Wenn dies erstes Blatt ist braucht RB4 noch nicht geprüft werden ...
blackHeight = bheight;
// aber der Referenzwert für die Schwarzhöhe muss gemerkt werden.
return true;
// Regel RB3: externe Knoten sind immer schwarz (RB2)
}
else
// Alle weiteren Blätter müssen die gleiche
return (blackHeight==bheight) ? true : false; // Schwarzhöhe haben
}
if( r->left ) isRBT = isRBT && isRBTreeR(r->left, bheight ); // Blätter im linken Teilb. suchen
if( r->right ) isRBT = isRBT && isRBTreeR(r->right, bheight ); // Blätter im rechten Teilb. suchen
if( r->color==RED ) { // RB3 prüfen: Rote Väter haben stets 2 schwarze Knaben
if(r->left)
isRBT = isRBT && ( r->left->color==BLACK );
if(r->right)
isRBT = isRBT && ( r->right->color==BLACK );
}
return isRBT;
}
10.01.2010
Nichtlineare Datenstruktur Baum
43
Algorithmus zum Testen des Einfügealgorithmus
FB Informatik
Prof. Dr. R.Nitsch
void testInsert_RB()
{
Tree t;
for( int m=0; m<100; ++m ) {
srand(m);
for ( int i=0; i<10000; ++i ){
t.insert_rb( rand() );
}
assert( t.isRBTree() );
t.clear();
}
}
Aufgabe: Zeichnen Sie die RBT-Baumstruktur nach dem Einfügen folgender Zahlenfolgen:
5362
5364
531
534
10.01.2010
Nichtlineare Datenstruktur Baum
44
Herunterladen