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)!
12.01.2009
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
12.01.2009
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.
12.01.2009
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
12.01.2009
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).
12.01.2009
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
12.01.2009
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)
12.01.2009
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
12.01.2009
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:
12.01.2009
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!
12.01.2009
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?
12.01.2009
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
12.01.2009
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
/*12.01.2009
Deklaration der Hilfsfunktionen findR, insertR,
eraseR*/
};
Suchen im BST
Rekursiver Algorithmus
für Suche im Baum
• Wenn die Wurzel ein externer Knoten ist,
liegt ein Suchfehler vor.
• Wenn die Wurzel
– einen kleineren Schlüssel enthält,
suche im linken Teilbaum
– einen größeren Schlüssel enthält,
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.
12.01.2009
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==0 ) {
// 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 == 0 ) 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);
} 12.01.2009
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
12.01.2009
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==0 ) ; // 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==0 ) ; // 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)
12.01.2009
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 == 0 ) ; // nothing to do
else {
// print left subtree
toStreamRpost( os, subtree->left );
// print right subtree
toStreamRpost( os, subtree->right );
// print key
os << subtree->key << ' ';
}
return;
}
g
7
c
a
1
3
k
f
j
2
4
6
5
post-order Reihenfolge
• Post-order Reihenfolge wird z.B.beim Löschen eines Baums angewendet
Tree::enum Order { preOrder,
inOrder, postOrder };
void toStream( ostream& os, Order order
if( !empty() )
switch(order) {
case inOrder:
toStreamRin ( os,
case preOrder:
toStreamRpre ( os,
case postOrder: toStreamRpost( os,
};
}
12.01.2009
=inOrder ) const {
root ); break;
root ); break;
root ); break;
Nichtlineare Datenstruktur Baum
m
19
Klasse Tree: Copy-Konstruktor und Zuweisung
Tree( const Tree& t ) {
root = 0; lmost=0; 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 == 0 )
; // 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
}
}
Pre-order
Reihenfolge
Anwendung im Zuweisungsoperator
Tree& Tree::operator=( const Tree& t ) {
if( this == &t ) return *this; // self assignment
this->clear();
this->copyR( t.root );
}
12.01.2009
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!=0 ) {
clearR( subtree->left ); //erase left subtree
post-order
clearR( subtree->right ); //erase right subtree
Reihenfolge
delete subtree ;
//erase root
subtree = 0;
//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(); }
12.01.2009
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 ) {
// Base case: "Da ist kein weiterer Knoten mehr!"
if ( subtree==0 ) return 0;
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==0 ) 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 );
}
12.01.2009
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==0 ) {
os << string(height, ' ' ) << '*' << endl; // Externer Knoten -> base case
return;
}
toStreamR( os, r->right, height+1 );
os << string(height, ' ' ) << r->info << endl;
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:
*
4
*
3
*
2
*
1
*
12.01.2009
*
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
D
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
12.01.2009
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
// Base case 1: Key k nicht gefunden; nichts gelöscht
if( subtree==0 ) return 0;
if( k < subtree->key )
// key k aus linkem Subtree entfernen
// Beachte: Ein Suchwert ist gefunden, wenn
return removeR( k, subtree->left );
// !(searched<current)&& !(current<searched)
// d.h. Aquivalenzoperator (key_type::operator==)
else if( subtree->key < k ) // key k aus rechtem Subtree entfernen // wird nicht benutzt und muss daher auch nicht
return removeR( k, subtree->right );
// definiert sein!
else
// Base case 2: Key an Wurzel des Subtree gefunden.
{
// Zeiger auf gefundenes Lösch-Element zur besseren Lesbarkeit
link d = subtree;
Bestimme Höhe des linken Teilbaums
if( d->left==0 && d->right==0 )
{
subtree = 0; // Schlüssel ausketten
delete d; --size;
// Schlüssel löschen
return 1;
}
12.01.2009
// Fall 1: Zu löschendes Element ist ein Blatt (keine Nachfolger)
vorher
nachher
D
Nichtlineare Datenstruktur Baum
25
Löschen von einzelnen Elementen im Baum
if( d->left!=0 && d->right!=0 ) {
return remove2child( d->left, d );
}
if( d->left == 0 ) {
sroot = d->right;
sroot->parent = d->parent;
}
else {
sroot = d->left;
sroot->parent = d->parent;
}
delete d; --size; return 1;
} //else
FB Informatik
Prof. Dr. R.Nitsch
// Fall 3: 2 Nachfolger
// 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
L
sroot
nachher
vorher
L
sroot
// Schlüssel löschen
}
size_type remove2child( link& subtree, link& d ) {
if( subtree->right != 0 ) // Solange rechts weitersuchen, bis Maximum gefunden
return remove2child( subtree->right, d );
vorher
else {
// sroot->right != 0; Maximum R gefunden
ple
link r = subtree;
// Verbessert Lesbarkeit
l
d->key = r->key;
// r nach d kopieren
sroot
if(r->left!=0)
// 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 d;
// r löschen
rl
}
12.01.2009
Nichtlineare Datenstruktur Baum
}
nachher
r
LL
s
LR
rl
26
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
12.01.2009
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
27
Implementation der Rotationstransformationen
void Tree::rotR( link& s ) {
// Linken Subtree mit Wurzel e von s abkoppeln
link e = s->left;
// Linker Teilbaum von s fehlt; nichts zu rotieren.
if( !e ) return;
// Wenn rechter Subtree von e existiert
if(e->right) {
s->left = e->right; // Rechter Subtree von e wird neuer linker Subtree von s
// Rückwärtslink von er nach s
e->right->parent = s;
// Nichts anzubinden -> ext. Knoten
} else s->left = 0;
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
}
FB Informatik
Prof. Dr. R.Nitsch
vorher
void Tree::rotL( link& b ) {
// Rechten Subtree mit Wurzel e von b abkoppeln
link e = b->right;
// Rechter Teilbaum von s fehlt; nicht zu rotieren.
if( !e ) return;
// Wenn rechter Subtree von e existiert
if(e->left) {
// Linker Subtree von e wird neuer rechter Subtree von b
b->right = e->left;
b->right->parent = b; // Rückwärtslink von el nach b
// Nichts anzubinden -> ext. Knoten
} else b->right = 0;
e->left = b;
// b als linken Subtree an e anbinden
e->parent = b->parent; // Rückwärtslink von b nach s
bl
b->parent = e;
// Rückwärtslink von b nach e
b = e;
// e an s anbinden
}
12.01.2009
Nichtlineare Datenstruktur Baum
b
nachher
b
s
e
e
s
sr
el
el
er
er
sr
parent
key K
left right
vorher
nachher
s
b
s
e
e
b
er
el
er
bl
el
28
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
12.01.2009
Nichtlineare Datenstruktur Baum
*
29
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==0 ) return 0;
// base case: externer Knoten erreicht; Löschschlüssel nicht gefunden!
if( k < r->info) return eraseR( k, r->left ); // im linken Teilbaum weiter suchen
else if ( r->info < 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( r->left, r->right );
// joinLR verbindet rechten und linken Teilbaum und gibt Zeiger
if(r) r->parent = d->parent;
// auf neue Wurzel zurück. Diese wird an den Vorgänger des
delete d; --size; return 1;
// Löschknotens angekoppelt
}
}
12.01.2009
Nichtlineare Datenstruktur Baum
30
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==0 ) return a;
// rechter Teilbaum ist leer; a ist schon Verbundbaum-Wurzel
min2root( b ); assert(b->left==0);
// Minimum in rechtem Teilbaum suchen und zur Wurzel machen
b->left = a;
// linken Teilbaum an Wurzel b anhängen
if( a!=0 ) a->parent = b;
// Wenn linker Teilbaum nicht leer ist
return b;
// Neue Verbundbaum-Wurzel zurückgeben
}
void min2root( link& r )
// Macht kleinsten Schlüsselwert eines (Teil)Baums zur Wurzel
{
if( r->left != 0 ) {
// In die linken Teilbäume hinabsteigen bis Blatt (=Nachfolger des Löschelements)
min2root( r->left );
// erreicht ist. Danach dieses Blatt mittels Rechtsrotationen zur Wurzel machen
rotR( r );
}
assert( r->left==0 ); return; // Nachbedingung: Der linke Teilbaum zur Wurzel muss leer sein
}
12.01.2009
Nichtlineare Datenstruktur Baum
31
Weitere Anwendungen von Rotationen
FB Informatik
Prof. Dr. R.Nitsch
• Eine Rotation vertauscht die Rolle einer
Wurzel
vorher
Wurzel (S bzw. B) mit einem ihrer
B
unmittelbaren Nachfolger (E).
• Eine Rechts-(Links-)Rotation wirkt auf die
S
Wurzel und den linken (rechten)
Nachfolger
E
• Durch eine Rechts-Rotation wird das rotierendes Element
rotierende Element und sein linker
sr
Teilbaum um eine Ebene angehoben
während die Wurzel (S) und ihr rechter
el
er
Teilbaum um eine Ebene abgesenkt wird.
Entsprechendes gilt für die Links-Rotation.
E≤ Schlüsselwerte <S
• Bäume mit nahezu gleich langen Pfaden zu
allen Blättern bezeichnet man als
ausgeglichene Bäume.
vorher
S
• Die Operationen Einfügen und Suchen sind
in ausgeglichenen Bäumen am schnellsten.
B
• Mit Rotations-Operationen können nicht
ausgeglichene (Sub)Bäume in ausgeglichene
(Sub)Bäume transformiert werden.
E
• Die Realisierung einer solchen
bl
Baumstruktur, die unausgeglichene
Zustände erkennt und mittels Rotationen
el
er
bl
behebt, nennt man auch red-black-tree.
Die Assoziativen Container der STL sind so
realisiert.
12.01.2009
nachher
B
E
S
el
er
sr
nachher
S
E
B
er
el
Rechtsrotation (oben) und Linksrotation (unten)
Nichtlineare Datenstruktur Baum
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.
12.01.2009
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.
• benötigt wird dazu:
– 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==0 || 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 = (subtree->left==0) ? 0 : countR(subtree->left); // n: Knotenanzahl im linken Teilbaum
if(n>k) {
// 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
12.01.2009
Nichtlineare Datenstruktur Baum
}
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, allerdings steigt bei diesen
der algorithmische Aufwand.
• 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
12.01.2009
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
Y
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!
};
12.01.2009
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!
12.01.2009
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!=0 ) assert( root->color == BLACK );
pair<_Iter,bool> success = insertR( t, root ); // insertR wird wiederverwendet
if ( !success.second ) 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==0 ) return success;
// x ist Baumwurzel; fertig
link g = v->parent;
// Grossvater von x
link o = 0;
// Onkel von x; noch unbekannt
Color ocolor;
// Farbe des Onkels
x->color = RED;
// Eingefügt wird immer zunächst als roter Knoten
if( g==0 ) return success;
// Vater ist Wurzelknoten und daher schwarz; x ist jetzt rot -> alles ok!
// Vater und Großvater existieren
12.01.2009
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==0 ) 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
12.01.2009
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!
12.01.2009
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!
12.01.2009
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 Urgrß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
Black is nobly
return success;
}
12.01.2009
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)
if (root!=0) 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==0 && r->right==0 ) { // 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
return (blackHeight==bheight) ? true : false; // Regel RB4 kann nur am Blatt geprüft werden
}
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;
}
12.01.2009
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
12.01.2009
Nichtlineare Datenstruktur Baum
44
Herunterladen