EINI-I Einführung in die Informatik für Naturwissenschaftler und Ingenieure I Kapitel 9 Claudio Moraga; Gisbert Dittrich FBI Unido
Kap 9: Durchlaufstrategien Vorl EINI-I" Gliederung Kapitel 9 Einfache Dateibehandlung Beispiel: Wörter zählen –Problem, Datenstruktur –Einfügen: Strategie + Implementierung –Alphabetisch geordnete Ausgabe: Strategie + Impl. Durchlaufstrategien bei Bäumen –In die Tiefe Inorder Präorder Postorder –In die Breite
Kap 9: Durchlaufstrategien Vorl EINI-I" Dateien Einfache Dateibehandlung –(externe Dateien, Öffnen, Lesen/Schreiben, Schließen). –Am Beispiel: Problem, die Wörter in einem gegebenen Text zu zählen und sie mit ihrer Häufigkeit alphabetisch geordnet auszugeben. Zunächst: Dateien Lies aus einer Eingabe-Datei, schreibe in eine Ausgabe-Datei. Am elementaren Beispiel. Programm
Kap 9: Durchlaufstrategien Vorl EINI-I" Dateien #include bindet die Bibliothek zur Dateibehandlung ein Bindung von Datei-Variablen (im Programm) an Dateien (im Dateisystem) beim Öffnen der Datei. ifstream *Eingabedatei; EingabeDatei = new ifstream(EinDat); Benutzung von Dateien: wie Standard Ein-/Ausgabe –ifstream : Eingabe - ofstream : Ausgabe Benutzung: wie cin (bzw. cout ) –*Eingabedatei >> gelesen;
Kap 9: Durchlaufstrategien Vorl EINI-I" Dateien Testen: (*Eingabedatei).eof() (liefert "true" genau dann, wenn das Ende der Datei erreicht ist; eof: end of file) !(*Eingabedatei).eof() (liefert "true" genau dann, wenn das Ende der Datei noch nicht erreicht ist) Analog: ofstream *AusgabeDatei; Werden Dateien zum Schreiben geöffnet, so geht meist ihr vorheriger Inhalt verloren.
Kap 9: Durchlaufstrategien Vorl EINI-I" // K9-P1 // Elementare Verwendung von Dateien // Vorsicht: nicht robust #include const int maxLen = 70; void Einlesen(ifstream *, ofstream *); void Schreiben(char *, ofstream *); 1
Kap 9: Durchlaufstrategien Vorl EINI-I" main() { ifstream *EingabeDatei; ofstream *AusgabeDatei; char EinDat[80], AusDat[80]; cout << "bitte Eingabedatei angeben: "; cin >> EinDat; cout << "bitte AusgabeDatei angeben: "; cin >> AusDat; cout << "Danke" << endl; EingabeDatei = new ifstream(EinDat); AusgabeDatei = new ofstream(AusDat); Einlesen(EingabeDatei, AusgabeDatei); cout << "fertig" << endl; } 2
Kap 9: Durchlaufstrategien Vorl EINI-I" void Einlesen(ifstream *ein, ofstream *aus) { char gelesen[maxLen]; *ein >> gelesen; while (!(*ein).eof()) { cout << "gelesen: " << gelesen << "\t\t\t\t"; Schreiben (gelesen, aus); *ein >> gelesen; } void Schreiben(char *Wort, ofstream *aus) { if (Wort != NULL) { *aus << Wort << endl;// Wort mit..... //..Zeilenumbruch in die Datei schreiben. cout << "geschrieben: " << Wort << endl; // Wort mit Zeilenumbruch... //..auf den Bildschirm schreiben. }}//3 Ausführen
Kap 9: Durchlaufstrategien Vorl EINI-I" Wörter zählen Problem: –zähle die Wörter in einem gegebenen Text, –gib sie mit ihrer Häufigkeit alphabetisch geordnet aus. Datenstruktur: –binärer Suchbaum (--> Wörter lassen sich ordnen) –erweitert um einen Zähler.
Kap 9: Durchlaufstrategien Vorl EINI-I" Datenstruktur zum Zählen von Wörtern text zaehler LSohnRSohn struct BinBaum { char text[maxLen]; int zaehler; BinBaum * LSohn, *RSohn; } ;
Kap 9: Durchlaufstrategien Vorl EINI-I" Strategie zum Einfügen Suchen nach einer Zeichenkette im binären Suchbaum –Zeichenkette nicht gefunden: neuen Knoten einfügen, Zähler zu 1 initialisieren –Zeichenkette gefunden: Zähler um 1 erhöhen
Kap 9: Durchlaufstrategien Vorl EINI-I" void strcpy(char *, char *); int strcmp(char *, char *); BinBaum * Einfuegen(BinBaum *B, char * k) { if (B == NULL) { BinBaum *Hilf = new BinBaum; strcpy(Hilf->text, k); Hilf->zaehler = 1; Hilf->LSohn = Hilf->RSohn = NULL; B = Hilf; Hilf = NULL; } else { int Vergl = strcmp(B->text,k); if (Vergl < 0) B->RSohn = Einfuegen(B->RSohn, k); else if (Vergl > 0) B->LSohn = Einfuegen(B->LSohn, k); else if (Vergl == 0) B->zaehler += 1; } return B; } Text noch nicht gesehen Text bekannt: einfügen, Zähler erhöhen
Kap 9: Durchlaufstrategien Vorl EINI-I" Alphabetisch geordnete Ausgabe Behauptung: Nachfolgender Durchlauf liefert als Resultat: –geordnete Ausgabe Durchlaufstrategie: –Durchlaufe den binären Suchbaum mit Wurzel w rekursiv wie folgt: Durchlauf durch den linken Unterbaum von w Ausdruck (der Nutzinfo) der Wurzel w Durchlauf durch den rechten Unterbaum von w
Kap 9: Durchlaufstrategien Vorl EINI-I" Beispiel (Baumdurchlauf)
Kap 9: Durchlaufstrategien Vorl EINI-I" Beweis Durch vollständige Induktion nach der Anzahl der Knoten –Der Induktionsbeginn (kein Knoten) ist erfüllt –Der Induktionsschritt: der linke Unterbaum wird geordnet ausgegeben (IV), dann wird die Wurzel ausgegeben, dann wird der rechte Unterbaum geordnet ausgegeben (IV). (Die Wurzel steht bzgl. der Ordnung "in der Mitte".)
Kap 9: Durchlaufstrategien Vorl EINI-I" Programmtext: Ausdrucken void KnotenDruck(BinBaum *, ofstream *); void Ausdrucken(BinBaum *K, ofstream *aus) { if (K != NULL) { Ausdrucken(K->LSohn, aus); KnotenDruck(K, aus); Ausdrucken(K->RSohn, aus); } } Programm
Kap 9: Durchlaufstrategien Vorl EINI-I" Anmerkungen Zugriff auf Ein- und Ausgabedateien wird über Zeiger auf ifstream- und ofstream- Variablen bewirkt. Initialisierungen –EingabeDatei = new ifstream(EinDat); –AusgabeDatei = new ofstream(AusDat); Varianten (Ausgabedateien als Konstante bekannt) z.B.: –ofstream *Ausgabe = new ofstream("von.aus");
Kap 9: Durchlaufstrategien Vorl EINI-I" BinBaum * Einfuegen(BinBaum *, char *); BinBaum * Einlesen(ifstream *ein) { BinBaum *bst = NULL; char gelesen[maxLen]; *ein >> gelesen; while (!(*ein).eof()) { bst = Einfuegen(bst, gelesen); *ein >> gelesen; } return bst; } Feinheiten: *ein und (*ein).eof()
Kap 9: Durchlaufstrategien Vorl EINI-I" Analog: Ausgabe void Schreiben(char *, int, ofstream *); void KnotenDruck(BinBaum *T, ofstream *aus){ Schreiben(T->text, T->zaehler, aus); } void Schreiben( char * s, int k, ofstream *aus ){ *aus << k << "\t\t\t" << s << endl; }
Kap 9: Durchlaufstrategien Vorl EINI-I" // K9-P2 Binärer Suchbaum // #include // beinhaltet void strcpy(char *, char *); // und int strcmp(char *, char *); const int maxLen = 70; struct BinBaum { char text[maxLen]; int zaehler; BinBaum * LSohn, *RSohn; }; // 1 Bibliothek für die Dateibehandlung
Kap 9: Durchlaufstrategien Vorl EINI-I" // Funktionsprototypen BinBaum * Einlesen(ifstream *); BinBaum * Einfuegen(BinBaum *, char *); void Ausdrucken(BinBaum *, ofstream *); void KnotenDruck(BinBaum *, ofstream *); void Schreiben(char *, int, ofstream *); // 2
Kap 9: Durchlaufstrategien Vorl EINI-I" main() { BinBaum *BST; ifstream *EingabeDatei; ofstream *AusgabeDatei; char EinDat[80], AusDat[80]; cout << "bitte Eingabedatei angeben: "; cin >> EinDat; cout << "bitte AusgabeDatei angeben: "; cin >> AusDat; cout << "Danke" << endl; EingabeDatei = new ifstream(EinDat); AusgabeDatei = new ofstream(AusDat); BST = Einlesen(EingabeDatei); Ausdrucken(BST, AusgabeDatei); cout << "fertig" << endl; } // 3
Kap 9: Durchlaufstrategien Vorl EINI-I" BinBaum * Einlesen(ifstream *ein) { BinBaum *bst = NULL; char gelesen[maxLen]; *ein >> gelesen; while (!(*ein).eof()) { bst = Einfuegen(bst, gelesen); *ein >> gelesen; }; return bst; } // 4
Kap 9: Durchlaufstrategien Vorl EINI-I" BinBaum * Einfuegen(BinBaum *B, char * k) { if (B == NULL) { BinBaum *Hilf = new BinBaum; strcpy(Hilf->text, k); Hilf->zaehler = 1; Hilf->LSohn = Hilf->RSohn = NULL; B = Hilf; Hilf = NULL;} else { int Vergl = strcmp(B->text,k); if (Vergl < 0) B->RSohn = Einfuegen(B->RSohn, k); else if (Vergl > 0) B->LSohn = Einfuegen(B->LSohn, k); else if (Vergl == 0) B->zaehler += 1;} return B;} // 5
Kap 9: Durchlaufstrategien Vorl EINI-I" void Ausdrucken(BinBaum *K, ofstream *aus) { if (K != NULL) { Ausdrucken(K->LSohn, aus); KnotenDruck(K, aus); Ausdrucken(K->RSohn, aus); } void KnotenDruck(BinBaum *T, ofstream *aus){ Schreiben(T->text, T->zaehler, aus); } void Schreiben(char * s, int k, ofstream *aus){ *aus << k << "\t\t\t" << s << endl; } // 6
Kap 9: Durchlaufstrategien Vorl EINI-I" Durchlauf durch Bäume Diese Art des Durchlaufs heißt Inorder-Durchlauf. w BLinksBRechts Inorder () Inorder(Wurzel BLinks) Druck(w) Inorder(Wurzel BRechts) =
Kap 9: Durchlaufstrategien Vorl EINI-I" w BLinksBRechts Präorder ( ) Präorder(Wurzel BLinks) Druck(w) Präorder(Wurzel BRechts) =
Kap 9: Durchlaufstrategien Vorl EINI-I" w BLinks BRechts Postorder ( ) Postorder(Wurzel BLinks) Druck(w) Postorder(Wurzel BRechts) =
Kap 9: Durchlaufstrategien Vorl EINI-I" Durchlaufstrategien Strategie bei allen drei Durchlaufarten heißt Tiefensuche: Es wird zunächst in die Tiefe und nicht in die Breite gegangen. Alternative: Breitensuche Trage den Baum "schichtenweise" ab.
Kap 9: Durchlaufstrategien Vorl EINI-I" Beispiel Breitensuche
Kap 9: Durchlaufstrategien Vorl EINI-I" Idee zur Implementation Verwalte die Knoten in einer "Warteschlange" ein Knoten wird gedruckt seine Söhne werden in die Warteschlange eingefügt bis die Warteschlange leer ist Initialisierung der Warteschlange mit der Wurzel des Baums.
Kap 9: Durchlaufstrategien Vorl EINI-I" Beispiel