1 18 Datenstrukturen 2006 Pearson Education, Inc. All rights reserved. 2 18.1 Einführung 18.2 Selbstbezügliche Klassen 18.3 Dynamische Speicherzuweisung und Datenstrukturen 18.4 Verkettete Listen 18.5 Stapel 18.6 Bäume 2006 Pearson Education, Inc. All rights reserved. 3 18.1 Einführung • Container-Klassen (oder Collection-Klassen) – Klassen, die dafür entworfen sind, eine Ansammlung von Objekten zu enthalten – Stellen üblicherweise Dienste wie Einfügen, Entfernen, Kopieren, Suchen, Sortieren, usw. zur Verfügung – Beispiele • Arrays • Stacks • Verkettete Listen • Bäume 2006 Pearson Education, Inc. All rights reserved. 4 18.1 Einführung • Dynamische Datenstrukturen für Container – Wachsen und schrumpfen während der Programmausführung – Verkettete Liste (‘List’) • Ansammlung von Daten, die in einer Reihe hintereinander angeordnet sind • Einfügen und Entfernen ist an jeder Stelle möglich – Stapel (‘Stack’) • Einfügen und Entfernen nur an einer Stelle (Oben, ‘Top’) • LIFO (last in, first out) 2006 Pearson Education, Inc. All rights reserved. 5 18.1 Einführung • Dynamische Datenstrukturen für Container – Binärbaum (‘Binary Tree’) • Ansammlung von Daten, in der jedes Element maximal zwei direkte Nachfolger hat • Erleichtert – Schnelles Suchen und Sortieren – Entfernen von Duplikaten • Eingesetzt für – Verzeichnisse von Dateisystemen – Compiler – Assoziative Arrays (STL) 2006 Pearson Education, Inc. All rights reserved. 6 18.2 Selbstbezügliche Klassen • Selbstbezügliche Klasse – Enthält einen Zeiger als Datenelement, der auf ein Objekt desselben Klassentyps zeigt – Beispiel • class Node { … Node* nextPtr; }; – Zeiger nextPtr ist ein Verbindungsglied (‘Link’) • Kann einen Knoten (‘Node’) mit einem anderen Knoten verbinden 2006 Pearson Education, Inc. All rights reserved. 7 Fig. 18.1 | Zwei miteinander verbundene Objekte einer selbstbezüglichen Klasse. 2006 Pearson Education, Inc. All rights reserved. 8 Häufiger Programmierfehler Wird der Link im letzten Knoten einer verketteten Datenstruktur nicht auf den Nullzeiger (nullptr) gesetzt, resultiert ein (möglicherweise ernster) logischer Fehler. 2006 Pearson Education, Inc. All rights reserved. 18.3 Dynamische Speicherzuweisung und Datenstrukturen 9 •new Operator – – – – Weist dynamisch Speicher für ein Objekt zu Übernimmt den Typ des Objekts als Argument Gibt die Speicheradresse des neuen Objekts zurück Beispiel • Node* newPtr = new Node( 10 ); – Reserviert sizeof( Node ) Bytes für ein neues NodeObjekt – Ruft den Node Konstruktor mit dem Argument 10 auf – Weist die Adresse des neuen Node-Objekts newPtr zu 2006 Pearson Education, Inc. All rights reserved. 18.3 Dynamische Speicherzuweisung und Datenstrukturen 10 •delete Operator – Ruft den Destruktor für ein Objekt auf und gibt den entsprechenden Speicher frei. – Beispiel • delete newPtr; – Ruft den Node Destruktor für das Objekt, auf das newPtr zeigt, auf – Gibt den Speicher für das Objekt, auf das newPtr zeigt, frei – Entfernt die Zeigervariable newPtr nicht – Wird delete für einen Nullzeiger aufgerufen, hat dies keinen Effekt. 2006 Pearson Education, Inc. All rights reserved. 11 18.4 Verkettete Listen • Verkettete Liste – Lineare Sequenz von selbstbezüglichen Klassenobjekten • Die einzelnen Objekte werden ‘Knoten’ genannt. • Sie sind durch Links (d.h. Zeiger) miteinander verbunden. – Auf die Liste wird über einen Zeiger auf den ersten Knoten zugegriffen. • Auf die folgenden Knoten wird jeweils über den Link des Vorgängerknotens zugegriffen. – Der Link im letzten Knoten wird üblicherweise auf den Nullzeiger (nullptr) gesetzt. – Zusätzliche Knoten werden bei Bedarf dynamisch erzeugt. 2006 Pearson Education, Inc. All rights reserved. 12 18.4 Verkettete Listen • Verkettete Liste – Vorteile gegenüber Arrays • Verkettete Listen sind dynamisch. – Die Länge kann genau dem Bedarf angepasst werden. • Neue Elemente können effektiv in eine sortierte Liste eingefügt werden. – Die vorhandenen Listenelemente müssen nicht bewegt werden. – Nachteil gegenüber Arrays • Der direkte Zugriff auf ein Listenelement ist nicht möglich. – Die Liste muss immer vom Anfang bis zum gewünschten Element durchgegangen werden. 2006 Pearson Education, Inc. All rights reserved. 13 Tipp zur Performanz Einfügen in und Entfernen eines Elements aus einem sortierten Array kann sehr zeitaufwändig sein - alle Elemente, die auf das eingefügte oder entfernte Element folgen, müssen entsprechend verschoben werden (O(n): linearer Zeitaufwand). Eine verkettete Liste erlaubt effizientes Einfügen und Entfernen überall in der Liste (O(1): konstanter Zeitaufwand). 2006 Pearson Education, Inc. All rights reserved. 14 Tipp zur Performanz Auf ein Arrayelement kann direkt zugegriffen werden, da die Adresse jedes Elements direkt aufgrund seiner Position relativ zum Arrayanfang berechnet werden kann (O(1): konstanter Zeitaufwand). Bei einer verketteten Liste kann jedes Element nur über das Durchlaufen aller Vorgängerelemente erreicht werden (O(n): linearer Zeitaufwand). 2006 Pearson Education, Inc. All rights reserved. 15 Betrachtung zum Software Engineering Die Auswahl einer Datenstruktur hängt üblicherweise von der Performanz bestimmter Operationen ab, die von einem Programm verwendet werden, und von der Ordnung, in der die Daten in der Datenstruktur gehalten werden. Beispielsweise ist es in der Regel effizienter, ein Element in eine sortierte verkettete Liste einzufügen als in ein sortiertes Array. 2006 Pearson Education, Inc. All rights reserved. 16 Fig. 18.2 | Graphische Darstellung einer Liste. 2006 Pearson Education, Inc. All rights reserved. 17 Tipp zur Fehlervermeidung Dem Link (Zeigerelement) eines neuen Knoten sollte immer der Nullzeiger (nullptr) zugewiesen werden. Zeiger sollten initialisiert werden, bevor sie verwendet werden. 2006 Pearson Education, Inc. All rights reserved. 1 // Fig. 18.3: Listnode.h 2 // Template ListNode class definition. 3 #ifndef LISTNODE_H 4 #define LISTNODE_H Outline 18 5 6 // forward declaration of class List required to announce that class 7 // List exists so it can be used in the friend declaration at line 13 8 template< typename NODETYPE > class List; Listnode.h (1 von 1) 9 10 template< typename NODETYPE> 11 class ListNode 12 { 13 friend class List< NODETYPE >; // make List a friend 14 public: 15 ListNode( const NODETYPE & info ) : data( info ), nextPtr( nullptr ) { } 16 NODETYPE getData() const { return data; } 17 private: 18 NODETYPE data; 19 ListNode * nextPtr; // next node in list data speichert einen Wert vom Typ NODETYPE 20 }; // end class ListNode 21 22 #endif nextPtr speichert einen Zeiger auf das nächste ListNode Objekt in der verketteten Liste 2006 Pearson Education, Inc. All rights reserved. 1 2 3 // Fig. 18.4: List.h // Template List class definition. #ifndef LIST_H 4 5 6 #define LIST_H 7 8 9 #include <sstream> #include <string> #include "listnode.h" // ListNode class definition Outline #include <iostream> 19 List.h (1 von 7) 10 11 template< typename NODETYPE > 12 class List 13 { 14 public: 15 List(); // constructor 16 17 18 ~List(); // destructor List( const List& ) = delete; // no copies of List-Objects allowed const List& operator=( const List& ) = delete; // no assignment allowed 19 20 21 void insertAtFront( const NODETYPE & ); void insertAtBack( const NODETYPE & ); bool removeFromFront( NODETYPE & ); 22 23 24 bool removeFromBack( NODETYPE & ); void insertInOrder( const NODETYPE & ); bool isEmpty() const; firstPtr zeigt auf den ersten ListNode in der List und lastPtr auf den letzten 25 std::string toString() const; 26 private: 27 ListNode< NODETYPE > * firstPtr; // pointer to first node 28 ListNode< NODETYPE > * lastPtr; // pointer to last node 29 }; // end class List 30 2006 Pearson Education, Inc. All rights reserved. 31 32 // default constructor 33 template< typename NODETYPE > Outline 34 List< NODETYPE >::List() 35 : firstPtr( nullptr ), lastPtr( nullptr ) 36 { 37 // empty body 38 } // end List constructor 39 Beide Zeiger mit Nullzeiger initialisieren 20 List.h (2 von 7) 40 // destructor 41 template< typename NODETYPE > 42 List< NODETYPE >::~List() 43 { 44 45 if( !isEmpty() ) { // List is not empty 46 47 48 std::cerr << "Destroying nodes ...\n"; 49 50 51 ListNode< NODETYPE > *tempPtr; 52 53 54 55 56 57 ListNode< NODETYPE > *currentPtr = firstPtr; while( currentPtr != nullptr ) { // delete remaining nodes tempPtr = currentPtr; std::cerr << tempPtr->data << '\n'; currentPtr = currentPtr->nextPtr; delete tempPtr; } // end while } // end if Alle ListNode Objekte im List Objekt müssen freigegeben werden, wenn das List Objekt zerstört wird 58 59 std::cerr << "All nodes destroyed\n\n"; 60 } // end List destructor 61 2006 Pearson Education, Inc. All rights reserved. 21 Fig. 18.4a | Graphische Darstellung der Operation insertAtFront. 2006 Pearson Education, Inc. All rights reserved. 22 Fig. 18.4b | Graphische Darstellung der Operation insertAtBack. 2006 Pearson Education, Inc. All rights reserved. 62 Outline 63 // insert node at front of list 64 template< typename NODETYPE > Neuer ListNode, der value als Datenelement enthält 65 void List< NODETYPE >::insertAtFront( const NODETYPE & value ) 66 { 67 ListNode< NODETYPE > * newPtr = new ListNode< NODETYPE >( value ); 68 69 if( isEmpty() ) // List is empty 70 firstPtr = lastPtr = newPtr; // new list has only one node 71 else // List is not empty 72 { 73 newPtr->nextPtr = firstPtr; // point new node to previous 1st 74 firstPtr = newPtr; // aim firstPtr at new node 75 } // end else 76 } // end function insertAtFront 77 78 // insert node at back of list 79 template< typename NODETYPE > 23 List.h (3 von 7) Für eine leere Liste werden firstPtr und lastPtr node auf newPtr gesetzt ‘Einfädeln’ des neuen Knotens in die Liste, so dass der neue Knoten auf den alten ersten Knoten zeigt und firstPtr auf den neuen Knoten 80 void List< NODETYPE >::insertAtBack( const NODETYPE & value ) 81 { 82 ListNode< NODETYPE > * newPtr = new ListNode< NODETYPE >( value ); 83 84 if( isEmpty() ) // List is empty 85 firstPtr = lastPtr = newPtr; // new list has only one node 86 else // List is not empty 87 { 88 lastPtr->nextPtr = newPtr; // update previous last node 89 lastPtr = newPtr; // new last node 90 } // end else 91 } // end function insertAtBack ‘Einfädeln’ des neuen Knotens in die Liste, so dass der alte letzte Knoten auf den 2006 Pearson Education, neuen Knoten zeigt und lastPtr auch Inc. All rights reserved. 24 Fig. 18.4c | Graphische Darstellung der Operation Operation removeFromFront. 2006 Pearson Education, Inc. All rights reserved. 92 Outline 93 // delete node from front of list 94 template< typename NODETYPE > 95 bool List< NODETYPE >::removeFromFront( NODETYPE & value ) 96 { 97 98 List.h return false; // delete unsuccessful } // end if 100 else { 101 Entfernt den ersten Knoten aus der Liste und kopiert den Wert des Knotens in den Referenzparameter if( isEmpty() ) { // List is empty 99 Ein zweiter Zeiger auf den ersten Knoten, (4 von 7) der für das Löschen gebraucht wird ListNode< NODETYPE > * tempPtr = firstPtr; // hold tempPtr to delete 102 103 104 105 106 firstPtr = lastPtr = nullptr; // no nodes remain after removal else firstPtr = firstPtr->nextPtr; // point to previous 2nd node 108 value = tempPtr->data; // return data being removed 109 delete tempPtr; // reclaim previous front node 110 return true; // delete successful } // end else 112 } // end function removeFromFront 113 Eine Liste mit nur einem Element ist nach dem Entfernen leer if ( firstPtr == lastPtr ) // List has one element 107 111 25 firstPtr zeigt jetzt auf den zweiten Knoten (den neuen ersten Knoten) Der Datenwert des entfernten Knotens wird in den Referenzparameter value kopiert Der Speicherplatz des entfernten Knotens wird freigegeben 2006 Pearson Education, Inc. All rights reserved. 26 Fig. 18.4d | Graphische Darstellung der Operation Operation removeFromBack. 2006 Pearson Education, Inc. All rights reserved. 114 // delete node from back of list 115 template< typename NODETYPE > 116 bool List< NODETYPE >::removeFromBack( NODETYPE & value ) 117 { 118 119 if( isEmpty() ) { // List is empty return false; // delete unsuccessful 120 } // end outer if 121 else { 122 Outline Entfernt den letzten Knoten aus der Liste und kopiert den Wert des Knotens in den Referenzparameter 27 List.h ListNode< NODETYPE > * tempPtr = lastPtr; // hold Ein zweiter Zeiger auf den letzten Knoten, (5 von 7) tempPtrder to für delete das Löschen gebraucht wird 123 124 125 if( firstPtr == lastPtr ) { // List has one element 126 } // end inner if 127 else { 128 wird die Adresse des ersten Knotens als Startwert für das Durchlaufen der Liste zugewiesen firstPtr = lastPtr = nullptr; // no nodes remain after removal currentPtr ListNode< NODETYPE > * currentPtr = firstPtr; 129 130 // locate second-to-last element 131 while( currentPtr->nextPtr != lastPtr ) 132 Durch die Liste laufen, bis currentPtr auf den vorletzten Knoten zeigt, der der neue letzte Knoten wird currentPtr = currentPtr->nextPtr; // move to next node 133 134 lastPtr = currentPtr; // remove last node 135 currentPtr->nextPtr = nullptr; // this is now the last node currentPtr wird der neue letzte Knoten 136 137 } // end inner else 138 value = tempPtr->data; // return value from old last node 139 delete tempPtr; // reclaim former last node 140 return true; // delete successful 141 } // end outer else 144 } // end function removeFromBack 143 Der Datenwert des entfernten Knotens wird in den Referenzparameter value kopiert Der Speicherplatz des entfernten Knotens wird freigegeben 2006 Pearson Education, Inc. All rights reserved. 145// insert node in sorted order 146 template< typename NODETYPE > 147 void List< NODETYPE >::insertInOrder( const NODETYPE & value ) Fügt einen Knoten in sortierter Outline Reihenfolge in die Liste ein 28 148 { 149 150 if( isEmpty() ) { // List is empty firstPtr = lastPtr = = new ListNode< NODETYPE >( value ); 151 } // end if 152 else { 153 if( 154 Für eine leere Liste werden firstPtr und lastPtr auf den neuen ListNode gesetzt. (6 von 7) !(firstPtr->data < value) ) { // same as: firstPtr->data >= value insertAtFront( value ); // value is the new first element 155 } // end else if 156 else if ( lastPtr->data < value ) { 157 insertAtBack( value ); // value is the new last element 158 } // end else if 159 else { 160 List.h Der neue Knoten kommt an den Anfang bzw. das Ende der Liste. ListNode< NODETYPE > * currentPtr = firstPtr->nextPtr, 161 162 * previousPtr = firstPtr, Durch * newPtr = new ListNode< NODETYPE >( value ); 163 // locate element in front of and behind new node 164 while( currentPtr != lastPtr && currentPtr->data < value ) { 165 previousPtr = currentPtr; 166 currentPtr = currentPtr->nextPtr; // move to next node 167 } // end while 168 previousPtr->nextPtr = newPtr; // newPtr gets linked between 169 newPtr->nextPtr = currentPtr; 170 171 die Liste laufen, bis currentPtr->data nicht mehr kleiner als value ist // previousPtr and currentPtr } // end inner else } // end outer else 172 } // end function insertInOrder Der neue Knoten wird zwischen previousPtr und currentPtr eingehängt. 173 2006 Pearson Education, Inc. All rights reserved. 174 // is List empty? Outline 175 template< typename NODETYPE > 176 bool List< NODETYPE >::isEmpty() const 29 177 { 178 return firstPtr == nullptr; List.h 179 } // end function isEmpty 180 (7 von 7) 181 // returns contents of List as a string 182 template< typename NODETYPE > 183 std::string List< NODETYPE >::toString() const 184 { 185 std::stringstream outStream; 186 if( isEmpty() ) { // List is empty 187 outStream << "The list is empty\n\n"; 188 } // end if 189 else { Durch die Liste iterieren und den Datenwert jedes Knotens ausgeben 190 ListNode< NODETYPE > *currentPtr = firstPtr; 191 outStream << "The list is:\n"; 192 while( currentPtr != nullptr ) { // get element data 193 outStream << ' ' << currentPtr->data; 194 195 currentPtr = currentPtr->nextPtr; } // end while 196 outStream << "\n\n"; 197 } // end else 198 return outStream.str(); 199 } // end function toString 200 201 #endif 2006 Pearson Education, Inc. All rights reserved. 1 // Fig. 18.5: fig18_05.cpp 2 // List class test program. 3 #include <iostream> 4 #include <string> 5 using namespace std; 6 7 #include "List.h" // List class definition 8 #include "Employee.h" // Employee class as defined in Kapitel 14 (fig14_05) 9 Outline 30 fig18_05.cpp (1 von 6) 10 // function to test a List 11 template< typename T > 12 void testList( List< T > & listObject, const string & typeName ) 13 { 14 cout << "Testing a List of " << typeName << " values\n"; 15 cout << "Enter one of the following:\n" 16 << " 1 to insert at beginning of list\n" 17 18 << " << " 2 to insert at end of list\n" 3 to delete from beginning of list\n" 19 << " 4 to delete from end of list\n" 20 << " 5 to insert an element in sorted order\n" 21 << " 6 to end list processing\n"; 22 23 int choice; // store user choice 24 T value; // store input value 25 26 do { // perform user-selected actions 27 cout << "? "; 28 cin >> choice; 2006 Pearson Education, Inc. All rights reserved. 29 30 31 switch( choice ) { case 1: // insert at beginning 32 cout << "Enter " << typeName << ": "; cin >> value; 33 34 listObject.insertAtFront( value ); break; 35 case 2: // insert at end 36 cout << "Enter " << typeName << ": "; cin >> value; 37 listObject.insertAtBack( value ); 38 break; 39 40 41 42 43 44 45 46 47 Outline 31 fig18_05.cpp (2 von 6) case 3: // remove from beginning if( listObject.removeFromFront( value ) ) cout << value << " removed from list\n"; break; case 4: // remove from end if( listObject.removeFromBack( value ) ) cout << value << " removed from list\n"; break; case 5: // insert in order 48 cout << "Enter " << typeName << ": "; cin >> value; 49 listObject.insertInOrder( value ); 50 break; 51 } // end switch 52 cout << listObject.toString(); 53 54 } while( choice != 6 ); // end do...while 55 cout << "End list test\n\n"; 56 } // end function testList 2006 Pearson Education, Inc. All rights reserved. 57 58 int main() 59 { Outline 60 // test List of int values 61 List< int > integerList; 62 testList( integerList, "integer" ); fig18_05.cpp 64 // test List of Employee values 65 List< Employee > EmployeeList; (3 von 6) 66 testList( EmployeeList, "Employee" ); 63 32 67 } // end main 2006 Pearson Education, Inc. All rights reserved. Testing a List of integer values Enter one of the following: 1 to insert at beginning of list 2 to insert at end of list 3 to delete from beginning of list 4 to delete from end of list 5 to insert an element in sorted order 6 to end list processing ? 1 Enter integer: 1 The list is: 1 Outline 33 fig18_05.cpp (4 von 6) ? 1 Enter integer: 2 The list is: 2 1 ? 2 Enter integer: 3 The list is: 2 1 3 ? 3 2 removed from list The list is: 1 3 ? 3 1 removed from list The list is: 3 2006 Pearson Education, Inc. All rights reserved. ? 4 3 removed from list The list is empty ? 6 End list test Testing a List of Employee values Enter one of the following: 1 to insert at beginning of list 2 to insert at end of list 3 to delete from beginning of list 4 to delete from end of list 5 to insert an element in sorted order 6 to end list processing ? 5 Enter Employee: Builder, Bob The list is: Builder, Bob Outline 34 fig18_05.cpp (5 von 6) ? 5 Enter Employee: Doe, John The list is: Builder, Bob Doe, John ? 5 Enter Employee: Builder, Alice The list is: Builder, Alice Builder, Bob Doe, John 2006 Pearson Education, Inc. All rights reserved. ? 5 Enter Employee: Doe, Jack The list is: Builder, Alice Builder, Bob Doe, Jack Doe, John ? 6 The list is: Builder, Alice Builder, Bob Doe, Jack Doe, John Outline 35 fig18_05.cpp (6 von 6) End list test Destroying nodes ... Builder, Alice Builder, Bob Doe, Jack Doe, John All nodes destroyed All nodes destroyed 2006 Pearson Education, Inc. All rights reserved. 36 18.4 Verkettete Listen • Doppelt verkettete Liste – Jeder Knoten hat einen Link auf den nächsten Knoten und einen Link auf den vorausgegangenen Knoten – Zwei “Anfangszeiger” – Einen auf den ersten Knoten, einen auf den letzten Knoten – Erlaubt Durchläufe sowohl vorwärts als auch rückwärts 2006 Pearson Education, Inc. All rights reserved. 37 Fig. 18.7 | Doppelt verkettete Liste. 2006 Pearson Education, Inc. All rights reserved. 38 18.5 Stapel • Stapel (Stack) – Knoten können nur ‘von oben’ (top) hinzugefügt und entfernt werden (LIFO: last-in, first-out) – Kann als eingeschränkte verkettete Liste implementiert werden • Link des letzten Knotens eines Stapels wird auf den Nullzeiger gesetzt (und bleibt auch immer der Nullzeiger), um den Boden des Stapels zu kennzeichnen – Erlaubte Operationen: Elementfunktionen push und pop • push fügt einen neuen Knoten oben auf dem Stapel hinzu • pop entfernt den obersten Knoten des Stapels und gibt seinen Wert zurück 2006 Pearson Education, Inc. All rights reserved. 39 18.5 Stapel • Stapel (Stack) – Realisierung als Komposition – Die öffentliche Schnittstelle der zugrunde liegenden Liste wird verborgen, indem nur noch privater Zugriff darauf erlaubt wird, und eine neue öffentliche Schnittstelle mit der Funktionalität eines Stack wird realisiert. 2006 Pearson Education, Inc. All rights reserved. 1 // Fig. 18.9: Stack.h 2 // Template Stack class definition with composed List object. 3 #ifndef STACK_H 4 #define STACK_H 5 6 #include <string> #include "List.h" // List class definition 7 8 template< typename STACKTYPE > 9 class Stack Outline 40 Stack.h (1 von 2) 10 { 11 public: 12 // no programmer-defined constructor; 13 // List default constructor does initialization of stackList data element 14 15 // push calls stackList object's insertAtFront member function 16 void push( const STACKTYPE &data ) 17 { 18 19 stackList.insertAtFront( data ); } // end function push 20 21 // pop calls stackList object's removeFromFront member function 22 bool pop( STACKTYPE &data ) 23 { 24 25 return stackList.removeFromFront( data ); } // end function pop 2006 Pearson Education, Inc. All rights reserved. 26 Outline 27 28 // isEmpty calls stackList object's isEmpty member function bool isEmpty() const 29 30 { 31 } // end function isEmpty 32 33 Stack.h // toString calls stackList object's toString member function 34 35 std::string toString() const { (2 von 2) 36 37 return stackList.toString(); } // end function toString 41 return stackList.isEmpty(); 38 private: 39 List< STACKTYPE > stackList; // composed List object 40 }; // end class Stack 41 42 #endif Die Implementierung des Stack Klassentemplate enthält das List< STACKTYPE > Objekt stackList 2006 Pearson Education, Inc. All rights reserved. 1 2 3 4 // Fig. 18.10: fig18_10.cpp // Template Stack class test program. 5 6 using namespace std; 7 8 #include "Stack.h" // Stack class definition Outline #include <iostream> 11 12 Stack< int > intStack; // create Stack of ints 13 14 15 16 cout << "processing an integer Stack" << endl; // push integers onto intStack for( int i = 0; i < 3; ++i ) { 17 18 19 intStack.push( i ); cout << intStack.toString(); } // end for 20 21 int popInteger; // store int popped from stack 22 23 // pop integers from intStack 24 25 while( !intStack.isEmpty() ) { intStack.pop( popInteger ); 28 fig18_10.cpp (1 von 3) 9 int main() 10 { 26 27 42 Ablegen der ganzen Zahlen von 0 bis 2 auf intStack Entfernen der ganzen Zahlen von 0 bis 2 von intStack cout << popInteger << " popped from stack" << endl; cout << intStack.toString(); } // end while 2006 Pearson Education, Inc. All rights reserved. 29 30 31 Stack< double > doubleStack; // create Stack of doubles double value = 1.1; Outline 43 32 33 34 cout << "processing a double Stack" << endl; 35 // push floating-point values onto doubleStack 36 37 for( int j = 0; j < 3; ++j ) { doubleStack.push( value ); 38 39 40 fig18_10.cpp (2 von 3) Ablegen der Werte 1.1, 2.2, 3.3 auf doubleStack cout << doubleStack.toString(); value += 1.1; } // end for 41 42 43 44 double popDouble; // store double popped from stack 45 while( !doubleStack.isEmpty() ) { // pop floating-point values from doubleStack Entfernen der Werte 3.3, 2.2 und 1.1 von doubleStack 46 47 doubleStack.pop( popDouble ); cout << popDouble << " popped from stack" << endl; 48 cout << doubleStack.toString(); 49 } // end while 50 } // end main 2006 Pearson Education, Inc. All rights reserved. processing an integer Stack The list is: 0 Outline 44 The list is: 1 0 The list is: 2 1 0 2 popped from stack The list is: 1 0 fig18_10.cpp 1 popped from stack The list is: 0 (3 von 3) 0 popped from stack The list is empty processing a double Stack The list is: 1.1 The list is: 2.2 1.1 The list is: 3.3 2.2 1.1 3.3 popped from stack The list is: 2.2 1.1 2.2 popped from stack The list is: 1.1 1.1 popped from stack The list is empty All nodes destroyed All nodes destroyed 2006 Pearson Education, Inc. All rights reserved. 45 18.6 Bäume • Baum (Tree) – Nichtlineare, zweidimensionale Datenstruktur – Knoten von Bäumen enthalten zwei oder mehrere Links • Knoten von Binärbäumen enthalten genau zwei Links 2006 Pearson Education, Inc. All rights reserved. 46 18.6 Bäume • Baum – Terminologie • Wurzelknoten – Erster Knoten in einem Baum • Kindknoten – Mit einem übergeordneten Knoten (seinem Elternknoten) verlinkter Knoten – In einem Binärbaum gibt es einen rechten und einen linken Kindknoten • Teilbaum – Baum, der dadurch definiert wird, dass ein Kindknoten als Wurzel seines eigenen Baums genommen wird • Blattknoten – Knoten ohne Kindknoten 2006 Pearson Education, Inc. All rights reserved. 47 Fig. 18.14 | Graphische Darstellung eines Binärbaums. 2006 Pearson Education, Inc. All rights reserved. 48 18.6 Bäume • Binärer Suchbaum – Die Werte in jedem linken Teilbaum sind kleiner als der Wert seines Elternknoten. – Die Werte in jedem rechten Teilbaum sind größer als der Wert seines Elternknoten. – Kann auf sechs Arten rekursiv durchlaufen werden, davon sind die drei wichtigsten: • Inorder – Linker Teilbaum, aktueller Knoten, rechter Teilbaum • Preorder – Aktueller Knoten, linker Teilbaum, rechter Teilbaum • Postorder – Linker Teilbaum, rechter Teilbaum, aktueller Knoten 2006 Pearson Education, Inc. All rights reserved. 49 Fig. 18.15 | Ein binärer Suchbaum. 2006 Pearson Education, Inc. All rights reserved. 1 2 3 // Fig. 18.16: Treenode.h // Template TreeNode class definition. #ifndef TREENODE_H 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #define TREENODE_H // forward declaration of class Tree template< typename NODETYPE > class Tree; Outline 50 Treenode.h (1 von 1) // TreeNode class-template definition template< typename NODETYPE > class TreeNode Tree< NODETYPE > wird als friend von { TreeNode< NODETYPE > deklariert friend class Tree< NODETYPE >; public: // constructor TreeNode( const NODETYPE &d ) : leftPtr( nullptr ), // pointer to left subtree Dieser Knoten wird als Blattknoten data( d ), // tree node data mit data-Wert d initialisiert rightPtr( nullptr ) // pointer to right substree { } // return copy of node's data NODETYPE getData() const { return data; } private: TreeNode * leftPtr; // pointer to left subtree NODETYPE data; 26 TreeNode * rightPtr; // pointer to right subtree 27 }; // end class TreeNode Die Zeiger leftPtr und rightPtr zeigen auf 28 29 #endif den linken bzw. rechten Teilbaum des Knotens 2006 Pearson Education, Inc. All rights reserved. 1 2 3 4 // Fig. 18.17: Tree.h // Template Tree class definition. #ifndef TREE_H #define TREE_H Outline 5 6 7 #include <iostream> #include <iomanip> Tree.h 8 9 10 11 #include #include #include #include (1 von 10) <sstream> <string> <new> "Treenode.h" 51 12 13 // Tree class-template definition 14 template< typename NODETYPE > class Tree 15 { 16 public: 17 Tree(); // constructor 18 ~Tree(); // destructor 19 20 21 22 Tree( const Tree& ) = delete; // no copies of Tree-objects allowed const Tree& operator=( const Tree& ) = delete; // no assignment allowed void insertNode( const NODETYPE & ); std::string preOrderTraversal() const; 23 24 25 std::string inOrderTraversal() const; std::string postOrderTraversal() const; TreeNode< NODETYPE > * binaryTreeSearch( const NODETYPE & ) const; 26 std::string toString() const; 27 void deleteNode( const NODETYPE & ); 28 private: 29 TreeNode< NODETYPE > * rootPtr; 2006 Pearson Education, Inc. All rights reserved. 30 31 32 // utility functions void insertNodeHelper( TreeNode< NODETYPE > *&, const NODETYPE & ); 33 34 35 void preOrderHelper( TreeNode< NODETYPE > *, std::stringstream & ) const; void inOrderHelper( TreeNode< NODETYPE > *, std::stringstream & ) const; void postOrderHelper( TreeNode< NODETYPE > *, std::stringstream & ) const; 36 37 38 TreeNode< NODETYPE > * binarySearchHelper( TreeNode< NODETYPE > *, const NODETYPE & ) const; void toStringHelper( TreeNode< NODETYPE >*, int, std::stringstream& ) const; 39 40 41 bool deleteNodeHelper( TreeNode< NODETYPE > *&, const NODETYPE & ); void replaceNodeHelper( TreeNode< NODETYPE > *&, TreeNode< NODETYPE > *& ); void destructorHelper( TreeNode< NODETYPE > *&); Outline 52 Tree.h (2 von 10) 42 }; // end class Tree 43 44 // constructor 45 template< typename NODETYPE > 46 Tree< NODETYPE >::Tree() 47 { 48 rootPtr = nullptr; // indicate tree is initially empty 49 } // end Tree constructor 50 51 // destructor 52 template< typename NODETYPE > 53 Tree< NODETYPE >::~Tree() 54 { 55 std::cerr << "Destroying nodes ...\n"; 56 destructorHelper( rootPtr ); 57 std::cerr << "All nodes destroyed\n\n"; 58 } // end Tree destructor 59 2006 Pearson Education, Inc. All rights reserved. 60 // insert node in Tree Outline 61 template< typename NODETYPE > 62 void Tree< NODETYPE >::insertNode( const NODETYPE & value ) 53 63 { 64 insertNodeHelper( rootPtr, value ); 65 } // end function insertNode Tree.h 66 67 // utility function called by insertNode; receives a reference to a 68 // pointer so that the function can modify the pointer arguments's value 69 template< typename NODETYPE > ptr ist eine Referenz für einen Zeiger auf einen TreeNode 70 void Tree< NODETYPE >::insertNodeHelper( 71 (3 von 10) TreeNode< NODETYPE > *& ptr, const NODETYPE & value ) 72 { 73 // subtree is empty; create new TreeNode containing value 74 if( ptr == nullptr ) { 75 ptr = new TreeNode< NODETYPE >( value ); 76 } // end if 77 else { // subtree is not empty 78 // data to insert is less than data in current node 79 if( value < ptr->data ) { 80 Ein neuer TreeNode wird erzeugt, initialisiert und dem Originalzeiger in der aufrufenden Funktion zugewiesen insertNodeHelper( ptr->leftPtr, value ); 81 } // end if 82 else { Rekursiver Aufruf von insertNodeHelper 83 // data to insert is greater than data in current node 84 if( ptr->data < value ) 85 86 87 88 89 insertNodeHelper( ptr->rightPtr, value ); else // duplicate data value ignored std::cerr << value << " dup" << endl; } // end else } // end else 90 } // end function insertNodeHelper 91 Falls der neu einzusetzende Wert identisch mit dem Wert im Elternknoten ist, wird der Wert nicht eingesetzt 2006 Pearson Education, Inc. All rights reserved. 92 93 94 95 96 97 // begin preorder traversal of Tree template< typename NODETYPE > std::string Tree< NODETYPE >::preOrderTraversal() const { std::stringstream outStream; 98 preOrderHelper( rootPtr, outStream ); 99 return outStream.str(); 100 } // end function preOrderTraversal 101 102 // utility function to perform preorder traversal of Tree Outline 54 Tree.h (4 von 10) 103template< typename NODETYPE > 104 void Tree< NODETYPE >::preOrderHelper( TreeNode< NODETYPE > * ptr, 105 std::stringstream & outStream ) const 106 { 107 if( ptr != nullptr ) { 108 outStream << ptr->data << ' '; // process node 109 preOrderHelper( ptr->leftPtr, outStream ); // traverse left subtree 110 preOrderHelper( ptr->rightPtr, outStream ); // traverse right subtree 111 } // end if 112 } // end function preOrderHelper Schreibt die Knoten in einer Reihenfolge, die beim Einlesen mit insertNode wieder genau den gleichen Baum ergibt 2006 Pearson Education, Inc. All rights reserved. 113 Outline 114 // begin inorder traversal of Tree 115 template< typename NODETYPE > 55 116 std::string Tree< NODETYPE >::inOrderTraversal() const 117 { Tree.h 118 std::stringstream outStream; 119 inOrderHelper( rootPtr, outStream ); 120 return outStream.str(); (5 von 10) 121 } // end function inOrderTraversal 122 123 // utility function to perform inorder traversal of Tree 124 template< typename NODETYPE > 125 void Tree< NODETYPE >::inOrderHelper( TreeNode< NODETYPE > * ptr, 126 std::stringstream & outStream ) const 127 { 128 if( ptr != nullptr ) { 129 inOrderHelper( ptr->leftPtr, outStream 130 outStream << ptr->data << ' '; // process node 131 inOrderHelper( ptr->rightPtr, outStream 132 ); // traverse left subtree ); // traverse right subtree } // end if 133} // end function inOrderHelper Durchläuft den Baum in sortierter Reihenfolge 2006 Pearson Education, Inc. All rights reserved. 134 Outline 135// begin postorder traversal of Tree 136 template< typename NODETYPE > 56 137std::string Tree< NODETYPE >::postOrderTraversal() const 138{ Tree.h 139 std::stringstream outStream; 140 postOrderHelper( rootPtr, outStream ); 141 return outStream.str(); (6 von 10) 142 } // end function postOrderTraversal 143 144 // utility function to perform postorder traversal of Tree 145 template< typename NODETYPE > 146 void Tree< NODETYPE >::postOrderHelper( TreeNode< NODETYPE > * ptr, 147 std::stringstream & outStream ) const 148 { 149 if( ptr != nullptr ) { 150 postOrderHelper( ptr->leftPtr, outStream 151 postOrderHelper( ptr->rightPtr, outStream 152 outStream << ptr->data << ' '; // process node 153 ); // traverse left subtree ); // traverse right subtree } // end if 154 } // end function postOrderHelper Besucht alle Knoten des Baumes ‘von außen her’, d.h. die Blätter zuerst 2006 Pearson Education, Inc. All rights reserved. 155 // begin binary search Outline 156 template< typename NODETYPE > 157 TreeNode< NODETYPE > * Tree< NODETYPE >::binaryTreeSearch 158 159 { 160 57 ( const NODETYPE & val ) const return binarySearchHelper( rootPtr, val ); Tree.h 161 } // end function binaryTreeSearch 162 (7 von 10) 163 // do a binary search on the Tree; NODETYPE::operator< required 164 template< typename NODETYPE > 165 TreeNode< NODETYPE > * Tree< NODETYPE >::binarySearchHelper 166 ( TreeNode< NODETYPE > * ptr, const NODETYPE & value ) const 167 { 168 // if value is not found 169 if( ptr == nullptr ) 170 171 172 ptr enthält den Zeiger auf den aktuellen TreeNode return nullptr; if( value < ptr->getData() ) { // search value less than current data return binarySearchHelper( ptr->leftPtr, value ); Rekursiver Aufruf von binarySearchHelper 173 } // end if 174 else if( ptr->getData() < value ) { // value greater than current data 175 return binarySearchHelper( ptr->rightPtr, value ); 176 } // end else if 177 else { // match 178 179 return ptr; } // end else Der Zeiger auf den TreeNode, der den gesuchten Wert enthält, wird zurückgegeben 180 } // end function binarySearchHelper 181 2006 Pearson Education, Inc. All rights reserved. 186 // return the tree as a string Outline 187 template< typename NODETYPE > 188 std::string Tree< NODETYPE >::toString() const 189 { 190 std::stringstream outStream; 191 toStringHelper( rootPtr, 2, outStream ); 192 return outStream.str(); 193 } // end function toString Tree.h 194 195 // utility function to return the tree as a string 196 template< typename NODETYPE > 197 void Tree< NODETYPE >::toStringHelper( TreeNode< NODETYPE > * ptr, 198 int totalSpaces, std::stringstream & outStream ) const 199{ Leerzeichen 200 if( ptr != nullptr ) { 201 toStringHelper( ptr->rightPtr, totalSpaces + 5, outStream); 202 outStream << setw( totalSpaces ) << ptr->data << '\n'; 203 toStringHelper( ptr->leftPtr, totalSpaces + 5, outStream); 204 } // end if 205 } // end function outputTreeHelper 206 207 // delete node in tree 58 (8 von 10) für die Formatierung der Ausgabe 208 template< typename NODETYPE > 209 void Tree< NODETYPE >::deleteNode( const NODETYPE & value ) 210 { 211 std::cerr << "deletNode called for: " << value; 212 if( deleteNodeHelper( rootPtr, value ) ) 213 std::cerr << " <- node deleted! " << endl; 214 else 215 std::cerr << " <- node not found! " << endl; 216 } // end function binarySearchHelper 217 2006 Pearson Education, Inc. All rights reserved. 59 18.6 Bäume • Löschen einzelner Elemente – Drei Varianten sind möglich 1. Zu löschendes Element ist ein Blatt: Direktes Entfernen ist möglich. 2. Zu löschendes Element hat genau einen Nachfolger: Zu löschendes Element wird durch den Nachfolger ersetzt. 3. Zu löschendes Element hat zwei Nachfolger: Der Wert im zu löschenden Element wird durch den kleinsten Wert im rechten Teilbaum ersetzt. Danach wird das Element mit dem kleinsten Wert im rechten Teilbaum ausgehängt (d.h. durch seinen rechten Nachfolger ersetzt) und gelöscht. 2006 Pearson Education, Inc. All rights reserved. 218 // utility function called by deleteNode; receives a reference to a 219 // pointer so that the function can modify the pointer arguments's value 220 template< typename NODETYPE > Outline 60 221 bool Tree< NODETYPE >::deleteNodeHelper( 222 TreeNode< NODETYPE > *& ptr, const NODETYPE & value ) 223 { 224 TreeNode< NODETYPE > * tempPtr; 225 if( ptr == nullptr ) // data not found in tree; no deletion 226 227 return false; else 228 229 230 231 232 ptr ist eine Referenz Tree.h für einen Zeiger auf einen TreeNode possible (9 von 10) // data to delete is less than data in current node; move to left child: if( value < ptr->data ) return deleteNodeHelper( ptr->leftPtr, value ); Rekursiver Aufruf von deleteNodeHelper else // data to delete greater than data in current node; move to right child: 233 234 if( ptr->data < value ) return deleteNodeHelper( ptr->rightPtr, value ); 235 236 else { tempPtr = ptr; // node to be deleted 237 // one (or zero) left child: child node takes place of deleted node 238 239 if( tempPtr->rightPtr == nullptr ) ptr = tempPtr->leftPtr; 240 241 else // one right child: child node takes place of deleted node 242 if( tempPtr->leftPtr == nullptr ) 243 244 ptr = tempPtr->rightPtr; else // two children: replace value to be deleted 245 246 replaceNodeHelper( tempPtr, tempPtr->rightPtr ); delete tempPtr; 247 return true; 248 } 249 } // end function deleteNodeHelper Hilfsfunktion zum Austauschen der Werte zweier Knoten 2006 Pearson Education, Inc. All rights reserved. 250 251 // utility function called by deleteNodeHelper to replace value 252 // to be deleted with smallest value in right subtree Outline 61 253template< typename NODETYPE > 254 void Tree< NODETYPE >::replaceNodeHelper( 255 256{ 257 TreeNode< NODETYPE > *& tempPtr, TreeNode< NODETYPE > *& right ) if( right->leftPtr != nullptr ) 258 259 260 261 // look for smallest value in right subtree replaceNodeHelper( tempPtr, right->leftPtr ); Tree.h (10 von 10) else { // replace value to be deleted with smallest value in right subtree 262 tempPtr->data = right->data; 263 264 tempPtr = right; // now this node has to be deleted right = right->rightPtr; // unlink the node that will be deleted 265 } 266 } // end function replaceNodeHelper 267 268 // utility function called by destructor for deletion of subtrees 269 // using postorder traversal 270 template< typename NODETYPE > 271void Tree< NODETYPE >::destructorHelper( TreeNode< NODETYPE > *& ptr ) 272 { 273 if( ptr != nullptr ) { 274 destructorHelper( ptr->leftPtr ); // delete left subtree 275 276 destructorHelper( ptr->rightPtr ); // delete right subtree std::cerr << ptr->getData() << ' '; 277 278 delete ptr; // delete root ptr = nullptr; // empty tree: rootPtr == nullptr 279 } 280 } // end function destructorHelper 281 #endif 2006 Pearson Education, Inc. All rights reserved. 1 // Fig. 18.20: fig18_20.cpp 2 // Tree class test program. 3 #include <iostream> 4 #include <fstream> 5 #include <string> 6 #include <array> 7 using namespace std; Outline fig18_20.cpp 8 9 62 (1 von 5) #include "Tree.h" // Tree class definition 10 #include "Employee.h" // Employee class as defined in Kapitel 14 (fig14_05) 11 12 int main() 13 { Baum intTree vom Typ Tree< int > instanzieren 14 Tree< int > intTree; // create Tree of int values 15 array< int, 15 > intValues = 16 { 50, 25, 75, 12, 33, 67, 88, 10, 13, 35, 85, 68, 90, 52, 30 }; 17 18 // insert 15 integers to intTree 19 for( int i = 0; i < 15; ++i ) { 20 21 int–Werte in den Binärbaum einsetzen intTree.insertNode( intValues[ i ] ); } // end for 22 23 cout << "intTree: Preorder traversal\n"; 24 cout << intTree.preOrderTraversal(); 25 cout << "\nintTree: Inorder traversal\n"; 26 cout << intTree.inOrderTraversal(); 27 cout << "\nintTree: Postorder traversal\n"; 28 cout << intTree.postOrderTraversal(); intTree durchlaufen 29 2006 Pearson Education, Inc. All rights reserved. 30 Tree< Employee > employeeTree; // create Tree of Employees 31 char lastName[ 10 ], firstName[ 10 ]; Outline 32 33 FILE* cfPtr = fopen( "data.txt", "r" ); 34 if( cfPtr == NULL ) { Baum employeeTree vom Typ Tree< Employee > instanzieren 35 cerr << "File could not be opened." << endl; 36 exit( 1 ); // exit program if file could not be opened 37 } // end if 40 while( fscanf( cfPtr, "%s%s", lastName, firstName ) == 2 ) employeeTree.insertNode( Employee( firstName, lastName ) ); 41 42 cout << "\n\nemployeeTree: Inorder traversal\n "; 43 cout << employeeTree.inOrderTraversal(); 44 Employee–Werte in den Binärbaum einsetzen employeeTree aufsteigend sortiert durchlaufen 45 // search a value in intTree 46 int intValue; 47 cout << "\nEnter int value to search for: "; 48 cin >> intValue ; 49 // create a pointer with the value entered by the user 50 TreeNode< int > * ptr = intTree.binaryTreeSearch( intValue ); 51 if( ptr != nullptr ) // a value is found 52 53 54 fig18_20.cpp (2 von 5) 38 39 63 cout << ptr->getData() << " was found\n"; else // value not found cout << "Element was not found\n"; 55 2006 Pearson Education, Inc. All rights reserved. 56 // delete a value in intTree 57 cout << "\nEnter int value to delete: "; 58 cin >> intValue ; 59 // delete node with the value entered by the user 60 intTree.deleteNode( intValue ); 61 cout << "\nintTree : Inorder traversal\n"; 62 cout << intTree.inOrderTraversal(); 63 cout << endl; 64 cout << "\nThe tree is:\n"; 65 cout << intTree.toString(); 66 cout << endl << endl; Outline 64 fig18_20.cpp (3 von 5) 67 } // end main intTree: 50 25 12 intTree: 10 12 13 intTree: 10 13 12 Preorder traversal 10 13 33 30 35 75 67 52 68 88 85 90 Inorder traversal 25 30 33 35 50 52 67 68 75 85 88 90 Postorder traversal 30 35 33 25 52 68 67 85 90 88 75 50 employeeTree: Inorder traversal Austen, Kate Burke, Juliet Carlyle, Boone Dawson, Michael Faraday, Daniel Ford, James Hume, Desmond Jarrah, Sayid Kwon, Jin-Soo Kwon, Sun-Hwa Linus, Benjamin Littleton, Claire Lloyd, Walter Locke, Jonathan 2006 Pearson Education, Inc. All rights reserved. Pace, Charlie Reyes, Hugo Rousseau, Danielle Rutherford, Shannon Shephard, Christian Shephard, Jack Straume, Miles Widmore, Charles Widmore, Penelope Enter int value to search for: 2 Element was not found Outline 65 fig18_20.cpp (4 von 5) Enter int value to delete: 2 deleteNode called for: 2 <- node not found! intTree: Inorder traversal 10 12 13 25 30 33 35 50 52 67 68 75 85 88 90 The tree is: 90 88 85 75 68 67 52 50 35 33 30 25 13 12 10 2006 Pearson Education, Inc. All rights reserved. Enter int value to search for: 67 67 was found Enter int value to delete: 67 deleteNode called for: 67 <- node deleted! Outline Alternative für Suchen und Entfernen eines Elementes aus dem intTree intTree: Inorder traversal 10 12 13 25 30 33 35 50 52 68 75 85 88 90 fig18_20.cpp The tree is: (5 von 5) 90 66 88 85 75 68 52 50 35 33 30 25 13 12 10 2006 Pearson Education, Inc. All rights reserved. 67 18.6 Bäume • Anwendungen von binären Suchbäumen – Entfernen von Duplikaten • Duplikat folgt beim Einsetzen dem gleichen Pfad wie das Original • Duplikate können beim Vergleich mit dem Original verworfen werden (oder z.B. gezählt werden). – Suchen (in einem ‘ausgeglichenen’ binären Suchbaum) • Hat Laufzeit O(log n) – Jeder Vergleich eines Knotens mit dem Suchschlüssel eliminiert die Hälfte der Knoten. – Es sind maximal log2 n Vergleiche erforderlich. – Sortieren (‘binary tree sort’) • Das Inorder-Durchlaufen eines binären Suchbaums sorgt für eine Verarbeitung der Werte in ansteigender Ordnung. 2006 Pearson Education, Inc. All rights reserved.