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