Beispielblatt 2 186.813 VU Algorithmen und Datenstrukturen 1 VU 6.0 25. September 2013 Aufgabe 1 Gegeben sei ein binärer Suchbaum mit Werten im Bereich von 1 bis 1001. In diesem Baum wird nach der Zahl 363 gesucht. Überprüfen Sie für die angegebenen Folgen (a) bis (d), ob die Elemente in der angegebenen Reihenfolge Werte von Knoten repräsentieren können, die bei der Suche im binären Suchbaum travesiert wurden. Unterstützen Sie Ihre Argumentation jeweils mit einer Skizze: (a) h943, 217, 335, 712, 289, 397, 348, 363i (b) h1001, 199, 935, 240, 936, 273, 363i (c) h1000, 200, 898, 248, 889, 273, 361, 363i (d) h2, 398, 388, 211, 276, 385, 379, 278, 363i Lösung (a) Von den ersten Zahlen 943 − 712 der Zahlenfolge ist es durchaus möglich, dass die angegebene Folge Werte von Knoten in einem binären Suchbaum repräsentiert. Aber der Knoten mit dem Wert 289 ist falsch eingeordnet, denn dieser müsste sich eigentlich in dem linken Teilbaum von Knoten 335 befinden. ⇒ Für die gesamte Zahlenfolge h943, 217, 335, 712, 289, 397, 348, 363i ist es nicht möglich. (Der Wertebereich, den Knoten im linken Teilbaum von 712 annehmen können ist zwischen 335 − 712) 943 217 335 712 289 (b) Von den ersten Zahlen 1001 − 240 der Zahlenfolge ist es durchaus möglich, dass die angegebene Folge Werte von Knoten in einem binären Suchbaum repräsentiert. Aber der Knoten mit dem Wert 936 ist falsch eingeordnet, denn dieser müsste sich eigentlich in dem rechten Teilbaum von Knoten 935 befinden. ⇒ Für die gesamte Zahlenfolge h1001, 199, 935, 240, 936, 273, 363i ist es nicht möglich. (Der Wertebereich, den Knoten im rechten Teilbaum von 240 annehmen können ist zwischen 240 − 935) 1001 199 935 240 936 (c) Ja, diese Travesierungsreihenfolge h1000, 200, 898, 248, 889, 273, 361, 363i ist durchaus möglich, wenn es sich um einen binären Suchbaum handelt. 1000 200 898 248 889 273 361 363 (d) Ja, diese Travesierungsreihenfolge h2, 398, 388, 211, 276, 385, 379, 278, 363i ist durchaus möglich, wenn es sich um einen binären Suchbaum handelt. 2 2 398 388 211 276 385 379 278 363 Listing 1: Check ob es sich um gültige Suchbäume handelt. (Für Klasse bin tree.h siehe Listing 3) 1 2 // This Project is written in C ++11 , so use following command to compile it : // g ++ - ggdb - std = c ++11 -o bsp1 bsp1 . cpp 3 4 5 # include < iostream > # include " bin_tree . h " 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 int main (){ std :: cout << std :: endl << " Bsp 1. a " << std :: endl ; { // 1. a { bin_tree < int > t ; static const int inserts [] = {943 ,217 ,335 ,712}; std :: vector < int > ins ( inserts , inserts + sizeof ( inserts ) / sizeof for ( int i : ins ) { t . insert ( i ); } t . insert_manually (289 ,712 , true ); // insert the node 289 as leftchi if ( t . c h e c k _ b i n a r y _ s e a r c h _ t r e e ()) { std :: cout << " RICHTIG " << std :: endl ; } else { std :: cout << " FALSCH " << std :: endl ; } 23 std :: cout << std :: endl <<t . to_string (3) << std :: endl ; 24 25 26 27 28 } { bin_tree < int > t ; static const int inserts [] = {943 ,217 ,335 ,712 ,289}; 3 std :: vector < int > ins ( inserts , inserts + sizeof ( inserts ) / sizeof for ( int i : ins ) { t . insert ( i ); } std :: cout << std :: endl <<t . to_string (3) << std :: endl ; 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 } } std :: cout << std :: endl << " Bsp 1. b " << std :: endl ; { // 1. b { bin_tree < int > t ; static const int inserts [] = {1001 , 199 , 935 , 240}; std :: vector < int > ins ( inserts , inserts + sizeof ( inserts ) / sizeof for ( int i : ins ) { t . insert ( i ); } t . insert_manually (936 ,240 , false ); // insert the node 936 as rightc if ( t . c h e c k _ b i n a r y _ s e a r c h _ t r e e ()) { std :: cout << " RICHTIG " << std :: endl ; } else { std :: cout << " FALSCH " << std :: endl ; } 51 std :: cout << std :: endl <<t . to_string (3) << std :: endl ; 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 } { bin_tree < int > t ; static const int inserts [] = {1001 , 199 , 935 , 240 , 936}; std :: vector < int > ins ( inserts , inserts + sizeof ( inserts ) / sizeof for ( int i : ins ) { t . insert ( i ); } std :: cout << std :: endl <<t . to_string (3) << std :: endl ; } } std :: cout << std :: endl << " Bsp 1. c " << std :: endl ; { // 1. c { bin_tree < int > t ; static const int inserts [] = {1000 , 200 , 898 , 248 , 889 , 273 , 361 , std :: vector < int > ins ( inserts , inserts + sizeof ( inserts ) / sizeof for ( int i : ins ) { t . insert ( i ); } if ( t . c h e c k _ b i n a r y _ s e a r c h _ t r e e ()) { std :: cout << " RICHTIG " << std :: endl ; } else { std :: cout << " FALSCH " << std :: endl ; 4 } 77 78 std :: cout << std :: endl <<t . to_string (3) << std :: endl ; 79 } 80 } std :: cout << std :: endl << " Bsp 1. d " << std :: endl ; { // 1. d { bin_tree < int > t ; static const int inserts [] = {2 , 398 , 388 , 211 , 276 , 385 , 379 , 27 std :: vector < int > ins ( inserts , inserts + sizeof ( inserts ) / sizeof for ( int i : ins ) { t . insert ( i ); } if ( t . c h e c k _ b i n a r y _ s e a r c h _ t r e e ()) { std :: cout << " RICHTIG " << std :: endl ; } else { std :: cout << " FALSCH " << std :: endl ; } 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 std :: cout << std :: endl <<t . to_string (3) << std :: endl ; 97 } 98 } 99 100 return 0; 101 102 } Aufgabe 2 Gegeben sei folgender AVL-Baum direkt nach dem Einfügen eines neuen Elements: 10 8 6 2 16 14 22 18 Ist die AVL-Eigenschaft in diesem Baum verletzt? Falls ja, nennen Sie den Knoten der zuletzt eingefügt wurde, führen Sie die notwendigen Maßnahmen durch, um die AVL-Bedingung wieder herzustellen, und zeichnen Sie den Baum in seinem korrigierten Zustand. Führen Sie danach folgende Operationen in diesem AVL-Baum in der angegebenen Reihenfolge durch und zeichnen Sie den Baum nach jeder Operation. Achten Sie darauf, dass die AVLEigenschaft immer erhalten bleibt. 5 • Fügen Sie 3 in den AVL-Baum ein. • Fügen Sie 5 in den AVL-Baum ein. • Löschen Sie 6 aus dem AVL-Baum (verwenden Sie falls notwendig den Successor als Ersatzelement). Lösung Ja, die AVL-Eigenschaft in diesem Baum ist verletzt. Der Knoten 2 wurde als letztes eingefügt. Dadurch erfüllt der Teilbaum mit der Wurzel 8 nicht mehr die AVL-Eigenschaft. ⇒ Der Teilbuam mit der Wurzel 8 muss nach rechts rotiert werden. Dadurch erhält der Teilbaum das Element 6 als neue Wurzel. 10 6 16 2 8 14 22 18 • 3 in den AVL-Baum einfügen: Hier wird die 3 einfach als rechtes Kind von 2 eingefügt. Es sind keine weiteren Schritte notwendig. 10 6 16 2 8 14 22 3 18 • 5 in den AVL-Baum einfügen: Hier wird die 5 als rechtes Kind von 3 eingefügt und dadurch wird die AVL-Bedingung verletzt. ⇒ Der Teilbaum mit der Wurzel 2 wird nach links rotiert, wodurch die 3 die neue Wurzel dieses Teilbaums wird. 10 6 3 2 16 8 5 14 22 18 • 6 aus dem AVL-Baum löschen: Hier wird 6 aus dem Baum rausgelöscht und durch 8 ersetzt. Dadurch entsteht wieder ein ungültiger AVL-Baum, denn der Teilbaum mit der Wurzel 8 erfüllt nicht die AVL-Eigenschaft. ⇒ Der Teilbaum muss nach rechts rotiert werden. Dadurch wird die 3 zur neuen Wurzel und die 5 wird links an die 8 angehängt. 6 10 3 16 2 8 5 14 22 18 Listing 2: Check ob es sich um gültige Suchbäume handelt. (Für Klasse avl tree.h siehe Listing 4) 1 2 // This Project is written in C ++11 , so use following command to compile it : // g ++ - ggdb - std = c ++11 -o bsp2 bsp2 . cpp 3 4 5 # include < iostream > # include " avl_tree . h " 6 7 8 9 10 11 12 13 int main (){ avl_tree < int > t ; static const int inserts [] = {10 , 8 , 16 , 6 , 14 , 22 , 18}; std :: vector < int > ins ( inserts , inserts + sizeof ( inserts ) / sizeof ( inserts for ( int i : ins ) { t . insert ( i ); } 14 std :: cout << std :: endl <<t . to_string (2) << std :: endl ; 15 16 t . insert (2); 17 18 std :: cout << std :: endl <<t . to_string (2) << std :: endl ; 19 20 t . insert (3); 21 22 std :: cout << std :: endl <<t . to_string (2) << std :: endl ; 23 24 t . insert (5); 25 26 std :: cout << std :: endl <<t . to_string (2) << std :: endl ; 27 28 return 0; 29 30 } Aufgabe 3 Vergleichen Sie die Datenstrukturen doppelt verkettete, zyklische Liste (aufsteigend sortiert), doppelt verkettete, zyklische Liste (nicht sortiert), natürlicher binärer Suchbaum und AVL-Baum bezüglich ihres Zeitaufwandes für die Suche nach einem Schlüssel k und die 7 Bestimmung des größten Elementes im Best- und Worst-Case in Θ-Notation in Abhängigkeit der Anzahl n der gespeicherten Elemente. Lösung i) Suche nach dem Schlüssel k • aufsteigend sortierte doppelt verkettete Zyklische Liste Best-Case: Θ(1) Worst-Case: Θ(n/2) = Θ(n) • unsortierte doppelt verkettete Zyklische Liste Best-Case: Θ(1) Worst-Case: Θ(n) • natürlicher binärer Suchbaum Best-Case: Θ(1) Worst-Case: Θ(n) • AVL-Baum Best-Case: Θ(1) Worst-Case: Θ(log(n)) ii) Bestimmung des größten Elements • aufsteigend sortierte doppelt verkettete Zyklische Liste Best-Case: Θ(1) Worst-Case: Θ(1) • unsortierte doppelt verkettete Zyklische Liste Best-Case: Θ(1) Worst-Case: Θ(n) • natürlicher binärer Suchbaum Best-Case: Θ(1) Worst-Case: Θ(n) • AVL-Baum Best-Case: Θ(1) Worst-Case: Θ(log(n)) Aufgabe 4 Aus folgendem B-Baum der Ordnung 3 soll der Schlüssel 8 entfernt werden: 8 10 8 20 5 9 12 21 29 Zeichnen Sie den Baum nach jedem Schritt der Reorganisation, die notwendig ist, um wieder einen gültigen B-Baum zu erhalten (die leeren, schwarz dargestellten Blätter können bei der Zeichnung entfallen). Lösung i) Löschen des Elements mit dem Schlüssel 8 un erstellung eines neuen Knotens mit den beiden Schlüsseln 5 und 9: 10 8 5 20 9 12 21 29 ii) Das Element mit dem Schlüssel 20 nach oben verschieben: 10 5 9 20 12 21 29 iii) Die Schlüssel 10 und 20 sind nun die neue Wurzel: 10 5 9 20 12 21 29 9 Aufgabe 5 Geben Sie den Zustand einer Hashtabelle der Länge 13 an, wenn die Schlüssel h5, 1, 16, 22, 14, 29, 32, 27, 2i in gegebener Reihenfolge in die anfangs leere Datenstruktur eingefügt werden und ein offenes Hashverfahren mit der Hashfunktion h(k) = k mod 13 sowie (a) lineares Sondieren (Schrittweite 2) (b) Double Hashing (h0 (k) = 1 + (k mod 5)) verwendet wird. Vergleichen Sie die Anzahl der beim Einfügen betrachteten Hashtabellenplätze für die angegebenen Sondierungsfunktionen. Lösung Schlüssel und Wert der Hashfunktion: k h1 (k) h2 (k) 5 5 1 1 1 2 16 3 2 22 9 3 14 1 5 29 3 5 32 6 3 27 1 3 2 2 3 (a) lineares Sondieren (Schrittweite 2) i) Einfügen von 5 (da die Tabelle leer ist, ist dies ohne Probleme möglich) 1 Vergleich: 0 1 2 3 4 5 5 6 7 8 9 10 11 12 ii) Einfügen von 1 (da die Tabelle fast leer ist, ist dies ohne Probleme möglich) 1 Vergleich: 0 1 1 2 3 4 5 5 6 7 8 9 10 11 12 iii) Einfügen von 16 (da die Tabelle fast leer ist, ist dies ohne Probleme möglich) 1 Vergleich: 0 1 1 2 3 16 4 5 5 6 7 8 9 10 11 12 iv) Einfügen von 22 (da die Tabelle fast leer ist, ist dies ohne Probleme möglich) 1 Vergleich: 0 1 1 2 3 16 4 5 5 6 7 8 9 22 10 11 12 v) Einfügen von 14 (hier gibts Kollissionen mit 1, 3 und 5, aber 7 ist frei) 4 Vergleiche: 0 1 1 2 3 16 4 5 5 6 7 14 8 9 22 10 11 12 vi) Einfügen von 29 (hier gibts Kollissionen mit 3, 5, 7 und 9, aber 11 ist frei) 5 Vergleiche: 10 0 1 1 2 3 16 4 5 5 6 7 14 8 9 22 10 11 29 12 vii) Einfügen von 32 (hier gibts keine Kollissionen, somit ist das Einfügen auf 6 ohne Probleme möglich) 1 Vergleich: 0 1 1 2 3 16 4 5 5 6 32 7 14 8 9 22 10 11 29 12 viii) Einfügen von 27 (hier gibts Kollissionen mit 1, 3, 5, 7, 9 und 11, aber 0 ist frei) 7 Vergleiche: 0 27 1 1 2 3 16 4 5 5 6 32 7 14 8 9 22 10 11 29 12 ix) Einfügen von 2 (hier gibts keine Kollissionen, somit ist das Einfügen auf 2 ohne Probleme möglich) 1 Vergleich: 0 27 1 1 2 2 3 16 4 5 5 6 32 7 14 8 9 22 10 11 29 12 ⇒ 22 Vergleiche sind bei diesem offenen Hashverfahren notwendig. (b) Double Hashing (h0 (k) = 1 + (k mod 5)) i) Einfügen von 5 (da die Tabelle leer ist, ist dies ohne Probleme möglich) 1 Vergleich: 0 1 2 3 4 5 5 6 7 8 9 10 11 12 ii) Einfügen von 1 (da die Tabelle fast leer ist, ist dies ohne Probleme möglich) 1 Vergleich: 0 1 1 2 3 4 5 5 6 7 8 9 10 11 12 iii) Einfügen von 16 (da die Tabelle fast leer ist, ist dies ohne Probleme möglich) 1 Vergleich: 0 1 1 2 3 16 4 5 5 6 7 8 9 10 11 12 iv) Einfügen von 22 (da die Tabelle fast leer ist, ist dies ohne Probleme möglich) 1 Vergleich: 0 1 1 2 3 16 4 5 5 6 7 8 9 22 10 11 12 v) Einfügen von 14 (hier gibts eine Kollission mit 1, aber 6 ist frei) 2 Vergleiche: 0 1 1 2 3 16 4 5 5 6 14 7 8 9 22 10 11 12 vi) Einfügen von 29 (hier gibts eine Kollission mit 3, aber 8 ist frei) 2 Vergleiche: 0 1 1 2 3 16 4 5 5 6 14 7 8 29 9 22 10 11 12 vii) Einfügen von 32 (hier gibts Kollissionen mit 6 und 9, aber 12 ist frei) 3 Vergleich: 11 0 1 1 2 3 16 4 5 5 6 14 7 8 29 9 22 10 11 12 32 viii) Einfügen von 27 (hier gibts eine Kollission mit 1, aber 4 ist frei) 2 Vergleiche: 0 1 1 2 3 16 4 27 5 5 6 14 7 8 29 9 22 10 11 12 32 ix) Einfügen von 2 (hier gibts keine Kollissionen, somit ist das Einfügen auf 2 ohne Probleme möglich) 1 Vergleich: 0 1 1 2 2 3 16 4 27 5 5 6 14 7 8 29 9 22 10 11 12 32 ⇒ 14 Vergleiche sind bei diesem offenen Hashverfahren notwendig. ⇒ Die Sondierungsmethode Double-Hashing ist wesentlich Effizienter in diesem Fall (wie sonst meist auch), denn die 2. von der 1. unabhängigen Hashfunktion bewirkt eine viel bessere Streuung der Werte in der Tabelle. Aufgabe 6 Breitensuche ist ein Verfahren zum Durchsuchen bzw. Durchlaufen von Knoten eines Graphen ähnlich der in der Vorlesung behandelten Tiefensuche. Auch hier geht man von einem Startknoten u aus, allerdings unterscheiden sich Tiefen- und Breitensuche hinsichtlich der Reihenfolge, in der weitere Knoten des Graphen abgearbeitet bzw. besucht werden. Wir gehen im Folgenden von einem ungerichteten Graphen aus. Beginnend mit dem Startknoten u werden bei der Breitensuche zunächst alle zu u adjazenten Knoten besucht, d.h. alle Knoten v, für die eine Kante (u, v) im Graphen existiert; zusätzlich werden alle diese Knoten v in einer Warteschlange gespeichert. Die Breitensuche bearbeitet also zuerst immer alle direkt benachbarten Knoten und folgt nicht – wie die Tiefensuche – gleich einem Pfad in die Tiefe. Nachdem nun alle adjazenten Knoten von u betrachtet wurden, wird der erste Knoten der Warteschlange entnommen und für diesen das Verfahren wiederholt. Dies wird nun so lange fortgesetzt, bis entweder die Warteschlange leer ist oder bis – wenn man nach einem bestimmten Knoten sucht – dieser gefunden wurde. Wie auch bei der Tiefensuche werden durch Markieren bereits bearbeiteter Knoten Mehrfachbesuche verhindert. Gegeben sei nun die Datenstruktur Queue (Warteschlange), welche eine beliebige Menge an Objekten aufnehmen kann und diese gemäß der Reihenfolge ihres Einfügens zurück liefert. Folgende Operationen werden von einer Queue Q zur Verfügung gestellt: • Q.isEmpty(): Liefert true zurück, falls die Queue Q keine Elemente enthält, und false sonst. • Q.put(x): Fügt das Element x der Queue Q hinzu. • Q.get(): Entfernt das älteste Element in der Queue Q und liefert dieses zurück. Benutzen Sie die Queue, um eine nicht-rekursive Version der Breitensuche zu entwerfen. Beschreiben Sie erst in wenigen Worten den Ablauf Ihres Algorithmus und geben Sie diesen dann 12 in Pseudocode an. Die Queue können Sie dabei als Black Box“ betrachten, d.h., Sie können ” sie benutzen, ohne die genaue Funktionsweise explizit als Pseudocode ausarbeiten zu müssen. Lösung 1. Alle eventuell markierten Knoten entmarkieren und die Queue leeren. 2. Anschließend wird der Startknoten in die Queue gelegt und markiert. 3. Das Erste Element wird aus der Queue genommen und für diesen Knoten werden alle Nachbarknoten bestimmt, die dann sofern sie nicht bereits markiert wurden in die Queue gelegt und markiert werden. 4. Es wird solange der Schritt 3 wiederholt, bis die Queue leer ist. Algorithmus Breitensuche(G, s): Eingabe: Graph G = (V, E); Knoten s ∈ V Ausgabe: alle Knoten G, di von s aus erreichbar sind 1: Initialisierung: setze markiert[v] = 0 für alle v; // Löschen der Elemente in der Queue 2: solange Q.isEmpty() == F alsch { 3: Q.get(); 4: } 5: Q.put(s); 6: markiert[s] = 1; 7: solange Q.isEmpty() == F alsch { 8: v = Q.get(); 9: Ausgabe von v; 10: für alle Knoten w aus N (v) { 11: falls markiert[w] == 0 dann { 12: Q.put(w); 13: markiert[w] = 1; 14: } 15: } 16: } Aufgabe 7 Auf dem gegebenen Graphen wird die aus dem Skriptum bekannte Tiefensuche und die aus dem vorigen Übungsbeispiel bekannte Breitensuche durchgeführt. 13 Welche der folgenden Listen von besuchten Knoten können dabei in genau dieser Reihenfolge entstehen? Hinweis: Die Nachbarn eines Knotens können in beliebiger Reihenfolge abgearbeitet werden. Reihenfolge ABCDEFGHIK ABDCGHIKFE BADCGHIKFE ABCDHKFEIH KEFHIGCDAB CGIKEFHBDA Tiefensuche Breitensuche x x x x Aufgabe 8 Gegeben ist ein gerichteter Graph G = (V, E), ein Startknoten s ∈ V und ein Zielknoten t ∈ V . Die Distanzen zwischen den Knoten sind in der Adjazenzmatrix A gegeben. Entwerfen Sie einen Greedy-Algorithmus in Pseudocode, der (mit der Motivation einen möglichst kurzen Weg vom Startknoten s zum Zielknoten t zu finden) wie folgt vorgeht: Ausgehend vom Startknoten s wählt dieser Algorithmus bei jedem Knoten jeweils immer die kürzeste noch nicht verwendete Kante, um dem Zielknoten t auf einem zusammenhängenden Pfad näher zu kommen. Liefert dieser Algorithmus immer den kürzesten Pfad? Begründen Sie Ihre Antwort. Veranschaulichen Sie die Funktionsweise des Algorithmus anhand eines Beispiels (Graph mit 4 – 5 Knoten), bzw. geben Sie ein möglichst einfaches Gegenbeispiel an. Lösung Der Folgende Algorithmus verwendet eine einfach verkettete Liste L, die alle Kanten enthält, die der Algorithums auf dem Weg von Knoten s nach e gefunden hat. In die Liste kann mittels L.push back(e) ein Element eingefügt werden. Die Liste kann mit L.clear() gelöscht werden. Algorithmus Breitensuche(G, s): Eingabe: Graph G = (V, E) mit Gewichten we ∀e ∈ E; Knoten s, e ∈ V Ausgabe: möglichst kurzer Pfad von s nach e (ist aber nicht unbedingt der kürzeste) 14 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: Initialisierung: setze markiert[i] = 0 für alle i; // Löschen der Elemente in der Liste L.clear(); k = s; solange k 6= e { sdist=NULL; nextk=NULL; für alle Knoten l aus N (k) { edge = e(k, l); // edge enthält die Kante zwischen den Knoten k und l falls markiert[edge] == 0 UND (sdist == N U LL ODER w(edge) < sdist) dann { sdist=w(edge); // sdist enthält die Distanz zwischen den Knoten k und l nextk=l; } } falls sdist == N U LL dann { Fehler, es existiert kein Weg von s nach e. } k = nextk; markiert[edge] = 1; L.push back(edge); } Der in Abbildung 1 gezeigte Graph ist z.B. ein Paradebeispiel dafür, dass der Algorithmus nicht immer den kürzesten Pfad vom Knoten s nach e findet. (Denn der optimalste Weg wäre der direkte zwischen s und e.) Das Problem an diesem Algorithmus ist, dass er sich immer nur die lokalen Bestwerte vergleicht und keinen Blick fürs große ganze hat. A 2 5 E 6 start 16 S 15 4 B 15 C Abbildung 1: Beispiel für einen Graph, der sich nicht gut für den Greedy-Algorithmus eignet. A. Bin-Tree A.1. bin tree.h 15 1 2 Listing 3: Klasse bin tree (Für Klasse avl tree.h siehe Listing 4) # ifndef BIN_TREE_H # define BIN_TREE_H 3 4 5 6 7 8 9 # include # include # include # include # include # include < memory > < stdexcept > < string > < sstream > < vector > < iomanip > 10 11 # include < math .h > 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 template < typename T > class bin_tree { protected : template < typename U > class element { private : typedef U data_type ; public : element () = delete ; element ( data_type _data ); element ( data_type _data , element * _parent ); element ( data_type _data , element * _parent , element * _left , el data_type data ; int height ; element * left ; element * right ; element * parent ; ~ element (); }; template < typename U > class limit { private : typedef U data_type ; data_type lim ; bool undef ; public : limit (); limit ( data_type _limit ); data_type get (); bool is_undef (); void set ( data_type _limit ); void set_undef (); }; 46 47 public : 16 48 49 50 51 52 typedef T data_type ; typedef element < data_type > elem ; bin_tree (); bin_tree ( data_type data ); ~ bin_tree (); 53 54 55 56 57 virtual void insert ( data_type data ); void insert_manually ( data_type data , data_type parent_data , bool left_or_right ); void del ( data_type data ); 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 std :: string to_string ( int width =2) const ; std :: string inorder () const ; std :: string preorder () const ; std :: string postorder () const ; int get_height () const ; bool c h e c k _ b i n a r y _ s e a r c h _ t r e e () const ; /* void insert_manually ( data_type data , data_type parent_data , bool l data_type search ( data_type data ); void del ( data_type data ); */ protected : int get_height ( const elem * current ) const ; void calc_height ( elem * current ); void c a l c _ h e i g h t _ r e c u r s i v e ( elem * current ); 73 74 75 76 void set_left ( elem * current , elem * left ); void set_right ( elem * current , elem * right ); void set_parent ( elem * current , elem * parent ); 77 78 79 80 std :: string inorder ( const elem * current ) const ; std :: string preorder ( const elem * current ) const ; std :: string postorder ( const elem * current ) const ; 81 82 elem * root ; 83 84 85 86 87 88 private : virtual void insert ( data_type data , elem * parent ); void to_vector ( std :: vector < std :: vector < std :: shared_ptr < data_type > > > & const elem * current ) const ; elem * search ( data_type data , elem * current ) const ; 89 90 91 92 93 elem elem elem elem * minimum ( elem * current ) const ; * maximum ( elem * current ) const ; * successor ( elem * current ) const ; * predecessor ( elem * current ) const ; 94 95 bool c h e c k _ b i n a r y _ s e a r c h _ t r e e ( const elem *e , limit < data_type > ulimit , 17 96 }; 97 98 99 100 template < typename T > bin_tree <T >:: bin_tree ( data_type data ) : root ( new elem ( data )){} 101 102 103 template < typename T > bin_tree <T >:: bin_tree (): root ( nullptr ){} 104 105 106 107 108 109 110 template < typename T > bin_tree <T >::~ bin_tree (){ if ( root ) { delete root ; } } 111 112 113 114 115 116 117 118 119 template < typename T > int bin_tree <T >:: get_height ( const elem * current ) const { if ( current ) { return current - > height ; } else { return -1; } } 120 121 122 123 124 template < typename T > bool bin_tree <T >:: c h e c k _ b i n a r y _ s e a r c h _ t r e e () const { return c h e c k _ b i n a r y _ s e a r c h _ t r e e ( root , limit < data_type >() , limit < data_type } 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 template < typename T > bool bin_tree <T >:: c h e c k _ b i n a r y _ s e a r c h _ t r e e ( const elem *e , limit < data_type > ul if (! llimit . is_undef () && e - > data <= llimit . get ()) { return false ; } if (! ulimit . is_undef () && e - > data > ulimit . get ()) { return false ; } if (e - > left ) { bool res ( c h e c k _ b i n a r y _ s e a r c h _ t r e e (e - > left , limit < data_type >( e - > data ) , if (! res ) { return false ; } } if (e - > right ) { return res ( c h e c k _ b i n a r y _ s e a r c h _ t r e e (e - > right , ulimit , limit < data_type } return true ; 18 144 } 145 146 147 148 149 template < typename T > int bin_tree <T >:: get_height () const { return get_height ( root ); } 150 151 152 153 154 155 156 157 158 159 template < typename T > void bin_tree <T >:: calc_height ( elem * current ){ int height = -1; height = get_height ( current - > left ); if ( get_height ( current - > right ) > height ){ height = get_height ( current - > right ); } current - > height = ++ height ; } 160 161 162 163 164 165 166 167 168 169 170 template < typename T > void bin_tree <T >:: c a l c _ h e i g h t _ r e c u r s i v e ( elem * current ){ if ( current - > left ) { c a l c _ h e i g h t _ r e c u r s i v e ( current - > left ); } if ( current - > right ) { c a l c _ h e i g h t _ r e c u r s i v e ( current - > right ); } calc_height ( current ); } 171 172 173 174 175 176 177 178 179 template < typename T > void bin_tree <T >:: insert ( data_type data ){ if (! root ) { root = new elem ( data ); } else { insert ( data , root ); } } 180 181 182 183 184 185 186 187 188 189 190 191 template < typename T > void bin_tree <T >:: insert_manually ( data_type data , data_type parent_data , bool left_or_right ){ elem * parent_node ( search ( parent_data , root )); if ( parent_node ) { if ( left_or_right ) { set_left ( parent_node , new elem ( data )); } else { set_right ( parent_node , new elem ( data )); } } else { 19 std :: stringstream ss ; ss << " Element " << parent_data << " doesn ’t exist . " ; throw std :: runtime_error ( ss . str ()); 192 193 194 } 195 196 } 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 template < typename T > void bin_tree <T >:: insert ( data_type data , elem * parent ) { if ( data < parent - > data ) { if (! parent - > left ) { set_left ( parent , new elem ( data , parent )); } else { insert ( data , parent - > left ); } } else { if (! parent - > right ) { set_right ( parent , new elem ( data , parent )); } else { insert ( data , parent - > right ); } } } 214 215 216 217 218 219 220 221 222 223 224 template < typename T > void bin_tree <T >:: set_left ( elem * current , elem * left ){ current - > left = left ; calc_height ( current ); if ( current - > parent ) { for ( elem * p ( current - > parent ); p ; p =p - > parent ) { calc_height ( p ); } } } 225 226 227 228 229 230 231 232 233 234 235 template < typename T > void bin_tree <T >:: set_right ( elem * current , elem * right ){ current - > right = right ; calc_height ( current ); if ( current - > parent ) { for ( elem * p ( current - > parent ); p ; p =p - > parent ) { calc_height ( p ); } } } 236 237 238 239 template < typename T > void bin_tree <T >:: set_parent ( elem * current , elem * parent ){ current - > parent = parent ; 20 calc_height ( current ); for ( elem * p ( current - > parent ); p ; p =p - > parent ) { calc_height ( p ); } 240 241 242 243 244 } 245 246 247 248 249 250 251 252 253 254 template < typename T > std :: string bin_tree <T >:: to_string ( int width ) const { if ( root ) { // generate an empty - string for empty - elements std :: stringstream data_stream ; data_stream << std :: setfill ( ’ ’ ); data_stream . width ( width ); data_stream << " " ; std :: string empty ( data_stream . str ()); 255 std :: vector < std :: vector < std :: shared_ptr < data_type > > > svec ; std :: stringstream ret ; 256 257 258 to_vector ( svec , root ); 259 260 int step =1; int chars_last_line = pow (2 , root - > height )*( width +1) -1; for ( typename std :: vector < std :: vector < std :: shared_ptr < data_type > > >:: c it = svec . begin (); it != svec . end (); ++ it ,++ step ) { int pos = pow (2 , root - > height - step +1)* width ; for ( typename std :: vector < std :: shared_ptr < data_type > >:: const_itera tit != it - > end (); ++ tit ) { data_stream . str ( " " ); if (* tit ) { data_stream << std :: setw ( width ) << std :: setfill ( ’0 ’) << ** tit ; } else { data_stream << empty ; } ret << std :: setw ( pos ) << data_stream . str (); ret << std :: setw ( pos ) << " " ; } ret << std :: endl ; } 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 return ret . str (); } else { return " empty Tree " ; } 281 282 283 284 285 } 286 287 template < typename T > 21 288 289 290 291 292 293 void bin_tree <T >:: to_vector ( std :: vector < std :: vector < std :: shared_ptr < data_type > > > & svec , const elem * current ) const { std :: vector < std :: shared_ptr < data_type > > tmp_vec ; tmp_vec . push_back ( std :: shared_ptr < data_type >( new data_type ( current - > data ) svec . push_back ( tmp_vec ); 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 if ( current - > left || current - > right ) { if ( current - > left && current - > right ) { std :: vector < std :: vector < std :: shared_ptr < data_type > > > leftvec , righ to_vector ( leftvec , current - > left ); to_vector ( rightvec , current - > right ); typename std :: vector < std :: vector < std :: shared_ptr < data_type > > >:: c rit ( rightvec . begin ()) , lit = leftvec . begin (); for ( int i =0; i < current - > height ; i ++) { // because first element is set above ... std :: vector < std :: shared_ptr < data_type > > tmp ; if ( lit != leftvec . end ()) { tmp . insert ( tmp . end () , lit - > begin () , lit - > end ()); lit ++; } else { for ( int j =0; j < pow (2 , i )/2; j ++) { tmp . push_back ( std :: shared_ptr < data_type >()); } } if ( rit != rightvec . end ()) { tmp . insert ( tmp . end () , rit - > begin () , rit - > end ()); rit ++; } else { for ( int j =0; j < pow (2 , i )/2; j ++) { tmp . push_back ( std :: shared_ptr < data_type >()); } } svec . push_back ( tmp ); } } else if ( current - > left ) { std :: vector < std :: vector < std :: shared_ptr < data_type > > > leftvec ; to_vector ( leftvec , current - > left ); for ( typename std :: vector < std :: vector < std :: shared_ptr < data_type > > lit ( leftvec . begin ()); lit != leftvec . end (); lit ++) { std :: vector < std :: shared_ptr < data_type > > tmp ; tmp . insert ( tmp . end () , lit - > begin () , lit - > end ()); for ( int i =0; i < lit - > size (); i ++) { tmp . push_back ( std :: shared_ptr < data_type >()); } svec . push_back ( tmp ); } } else if ( current - > right ) { 22 std :: vector < std :: vector < std :: shared_ptr < data_type > > > rightvec ; to_vector ( rightvec , current - > right ); for ( typename std :: vector < std :: vector < std :: shared_ptr < data_type > > rit ( rightvec . begin ()); rit != rightvec . end (); rit ++) { std :: vector < std :: shared_ptr < data_type > > tmp ; for ( int i =0; i < rit - > size (); i ++) { tmp . push_back ( std :: shared_ptr < data_type >()); } tmp . insert ( tmp . end () , rit - > begin () , rit - > end ()); svec . push_back ( tmp ); } 335 336 337 338 339 340 341 342 343 344 345 } } else { // befuelle die restliche hoehe mit leeren zeichen ... if ( current - > parent ) { elem * tmp ( current - > parent ); int depth = 0; while ( tmp ) { depth ++; tmp = tmp - > parent ; } for ( int i = depth +1; i <= root - > height ; i ++) { std :: vector < std :: shared_ptr < data_type > > tmp ; int chars_line = pow (2 , i - depth ); for ( int j =0; j < chars_line ; j ++) { tmp . push_back ( std :: shared_ptr < data_type >()); } svec . push_back ( tmp ); } } } 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 } 367 368 369 370 371 372 373 374 375 template < typename T > std :: string bin_tree <T >:: inorder () const { if ( root ) { return inorder ( root ); } else { return " empty Tree " ; } } 376 377 378 379 380 381 382 template < typename T > std :: string bin_tree <T >:: inorder ( const elem * current ) const { std :: stringstream ret ; if ( current - > left ) { ret << inorder ( current - > left ); } 23 ret << current - > data << " , " ; if ( current - > right ) { ret << inorder ( current - > right ); } return ret . str (); 383 384 385 386 387 388 } 389 390 391 392 393 394 395 396 397 template < typename T > std :: string bin_tree <T >:: preorder () const { if ( root ) { return preorder ( root ); } else { return " empty Tree " ; } } 398 399 400 401 402 403 404 405 406 407 408 409 410 template < typename T > std :: string bin_tree <T >:: preorder ( const elem * current ) const { std :: stringstream ret ; ret << current - > data << " , " ; if ( current - > left ) { ret << preorder ( current - > left ); } if ( current - > right ) { ret << preorder ( current - > right ); } return ret . str (); } 411 412 413 414 415 416 417 418 419 template < typename T > std :: string bin_tree <T >:: postorder () const { if ( root ) { return postorder ( root ); } else { return " empty Tree " ; } } 420 421 422 423 424 425 426 427 428 429 430 template < typename T > std :: string bin_tree <T >:: postorder ( const elem * current ) const { std :: stringstream ret ; if ( current - > left ) { ret << postorder ( current - > left ); } if ( current - > right ) { ret << postorder ( current - > right ); } ret << current - > data << " , " ; 24 return ret . str (); 431 432 } 433 434 435 436 437 438 439 440 441 442 443 444 445 template < typename T > typename bin_tree <T >:: elem * bin_tree <T >:: search ( data_type data , elem * current if ( current - > data == data ) { return current ; } else if ( current - > left && data < current - > data ){ return search ( data , current - > left ); } else if ( current - > right ){ return search ( data , current - > right ); } else { return nullptr ; } } 446 447 448 449 450 451 452 453 454 template < typename T > typename bin_tree <T >:: elem * bin_tree <T >:: minimum ( elem * current ) const { if ( current - > left ) { return minimum ( current - > left ); } else { return current ; } } 455 456 457 458 459 460 461 462 463 template < typename T > typename bin_tree <T >:: elem * bin_tree <T >:: maximum ( elem * current ) const { if ( current - > right ) { return maximum ( current - > right ); } else { return current ; } } 464 465 466 467 468 469 470 471 472 473 474 475 476 template < typename T > typename bin_tree <T >:: elem * bin_tree <T >:: successor ( elem * current ) const { if ( current - > right ) { return minimum ( current - > right ); } else { if ( current - > parent && current - > parent - > right == current ) { return successor ( current - > parent ); } else { return current - > parent ; } } } 477 478 template < typename T > 25 479 480 481 482 483 484 485 486 487 488 489 typename bin_tree <T >:: elem * bin_tree <T >:: predecessor ( elem * current ) const { if ( current - > left ) { return maximum ( current - > left ); } else { if ( current - > parent && current - > parent - > left == current ) { return predecessor ( current - > parent ); } else { return current - > parent ; } } } 490 491 492 493 494 495 template < typename T > void bin_tree <T >:: del ( data_type data ){ elem * del ( search ( data , root )); if ( del ) { std :: unique_ptr < elem > r ; 496 497 498 499 500 501 502 if (! del - > left || ! del - > right ) { r . reset ( del ); } else { r . reset ( successor ( del )); del - > data = r - > data ; } 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 elem * p ; if (r - > left ) { p = r - > left ; } else { p = r - > right ; } if ( p ) { p - > parent = r - > parent ; } if (! r - > parent ) { root = p ; calc_height ( root ); } else { if ( r . get ()== r - > parent - > left ) { set_left (r - > parent , p ); } else { set_right (r - > parent , p ); } } 523 524 525 526 // this is importaint , if you forget this , every child would be delete r - > left = nullptr ; r - > right = nullptr ; 26 } else { std :: stringstream ss ; ss << " Element " << data << " doesn ’t exist . " ; throw std :: runtime_error ( ss . str ()); } 527 528 529 530 531 532 } 533 534 535 536 537 538 539 540 541 542 template < typename T > template < typename U > bin_tree <T >:: element <U >:: element ( data_type _data ) : data ( _data ) , parent ( nullptr ) , left ( nullptr ) , right ( nullptr ) , height (0) { 543 544 545 546 547 template < typename T > template < typename U > bin_tree <T >:: element <U >:: element ( data_type _data , element * _parent ) : data ( _data ) , parent ( _parent ) , left ( nullptr ) , right ( nullptr ) , height (0) { 548 549 550 551 552 553 template < typename T > template < typename U > bin_tree <T >:: element <U >:: element ( data_type _data , element * _parent , element * _left , element * _right ) : data ( _data ) , parent ( _parent ) , left ( _left ) , right ( _right ) , height (0) {} 554 555 556 557 558 559 560 561 562 563 564 template < typename T > template < typename U > bin_tree <T >:: element <U >::~ element (){ if ( left ) { delete left ; } if ( right ) { delete right ; } } 565 566 567 568 569 570 template < typename T > template < typename U > bin_tree <T >:: limit <U >:: limit () : lim () , undef ( true ) {} 571 572 573 574 template < typename T > template < typename U > bin_tree <T >:: limit <U >:: limit ( data_type _limit ) 27 575 : lim ( _limit ) , undef ( false ) {} 576 void set_undef (); 577 578 579 580 581 582 583 template < typename T > template < typename U > bool bin_tree <T >:: limit <U >:: is_undef (){ return undef ; } 584 585 586 587 588 589 template < typename T > template < typename U > U bin_tree <T >:: limit <U >:: get (){ return lim ; } 590 591 592 593 594 595 596 template < typename T > template < typename U > void bin_tree <T >:: limit <U >:: set ( data_type _limit ){ undef = false ; lim = _limit ; } 597 598 599 600 601 602 template < typename T > template < typename U > void bin_tree <T >:: limit <U >:: set_undef (){ undef = true ; } 603 604 # endif A.2. avl tree.h Listing 4: Klasse avl tree 1 2 # ifndef AVL_TREE_H # define AVL_TREE_H 3 4 # include " bin_tree . h " 5 6 7 8 9 10 11 12 13 14 template < typename T > class avl_tree : public bin_tree <T >{ private : typedef T data_type ; typedef typename bin_tree <T >:: elem elem ; public : avl_tree (): bin_tree <T >(){}; avl_tree ( data_type data ): bin_tree <T >( data ){}; virtual void insert ( data_type data ){ bin_tree <T >:: insert ( data );}; 28 15 private : virtual void insert ( data_type data , elem * parent ); 16 17 18 elem elem elem elem 19 20 21 22 23 * rotate_to_right ( elem * current ); * rotate_to_left ( elem * current ); * d o u b l e _ r o t a t e _ r i g h t _ l e f t ( elem * current ); * d o u b l e _ r o t a t e _ l e f t _ r i g h t ( elem * current ); }; 24 25 26 27 28 29 30 31 32 33 template < typename T > void avl_tree <T >:: insert ( data_type data , elem * parent ) { if ( data < parent - > data ) { if (! parent - > left ) { this - > set_left ( parent , new elem ( data , parent )); } else { insert ( data , parent - > left ); } this - > calc_height ( parent ); 34 35 36 37 38 if ( this - > get_height ( parent - > right ) - this - > get_height ( parent - > left ) == elem * father ( parent - > parent ); if ( this - > get_height ( parent - > left - > left ) > this - > get_height ( parent elem * tmp ( this - > rotate_to_right ( parent )); 39 40 this - > set_parent ( tmp , father ); 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 if ( father && father - > data > tmp - > data ){ this - > set_left ( father , tmp ); } else if ( father && father - > data <= tmp - > data ){ this - > set_right ( father , tmp ); } else { this - > root = tmp ; } } else { elem * tmp ( this - > d o u b l e _ r o t a t e _ l e f t _ r i g h t ( parent )); this - > set_parent ( tmp , father ); if ( father ){ this - > set_right ( father , tmp ); } else { this - > root = tmp ; } } } } else { if (! parent - > right ) { this - > set_right ( parent , new elem ( data , parent )); } else { 29 insert ( data , parent - > right ); 63 } this - > calc_height ( parent ); 64 65 66 if ( this - > get_height ( parent - > right ) - this - > get_height ( parent - > left ) == elem * father ( parent - > parent ); if ( this - > get_height ( parent - > right - > right ) > this - > get_height ( pare elem * tmp ( this - > rotate_to_left ( parent )); this - > set_parent ( tmp , father ); if ( father ){ this - > set_left ( father , tmp ); } else { this - > root = tmp ; } } else { elem * tmp ( this - > d o u b l e _ r o t a t e _ r i g h t _ l e f t ( parent )); this - > set_parent ( tmp , father ); this - > set_right ( father , tmp ); if ( father ){ this - > set_right ( father , tmp ); } else { this - > root = tmp ; } } } 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 } 88 89 } 90 91 92 93 94 95 96 97 98 template < typename T > typename avl_tree <T >:: elem * avl_tree <T >:: rotate_to_right ( elem * current ){ if ( current && current - > left ) { elem * tmp ( current - > left ); this - > set_left ( current , tmp - > right ); if ( current - > left ) { this - > set_parent ( current - > left , current ); } 99 this - > set_right ( tmp , current ); this - > calc_height ( current ); this - > calc_height ( tmp ); 100 101 102 103 tmp - > parent = nullptr ; current - > parent = tmp ; current = tmp ; 104 105 106 107 } return current ; 108 109 110 } 30 111 112 113 114 115 template < typename T > typename avl_tree <T >:: elem * avl_tree <T >:: rotate_to_left ( elem * current ){ if ( current && current - > right ) { elem * tmp ( current - > right ); 116 this - > set_right ( current , tmp - > left ); if ( current - > right ) { this - > set_parent ( current - > right , current ); } 117 118 119 120 121 tmp - > left = current ; this - > calc_height ( current ); this - > calc_height ( tmp ); 122 123 124 125 tmp - > parent = nullptr ; current - > parent = tmp ; current = tmp ; 126 127 128 } return current ; 129 130 131 } 132 133 134 135 136 137 template < typename T > typename avl_tree <T >:: elem * avl_tree <T >:: d o u b l e _ r o t a t e _ l e f t _ r i g h t ( elem * curre this - > set_left ( current , rotate_to_left ( current - > left )); return rotate_to_right ( current ); } 138 139 140 141 142 143 template < typename T > typename avl_tree <T >:: elem * avl_tree <T >:: d o u b l e _ r o t a t e _ r i g h t _ l e f t ( elem * curre this - > set_right ( current , rotate_to_right ( current - > right )); return rotate_to_left ( current ); } 144 145 # endif 31