Präsentation herunterladen
Die Präsentation wird geladen. Bitte warten
Veröffentlicht von:Magdalene Nentwig Geändert vor über 10 Jahren
1
Einführung in die Programmierung Wintersemester 2008/09 Prof. Dr. Günter Rudolph Lehrstuhl für Algorithm Engineering Fakultät für Informatik TU Dortmund
2
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 2 Kapitel 8: Elementare Datenstrukturen Inhalt Definition: Abstrakter Datentyp (ADT) ADT Keller ADT Schlange ADT Liste ADT Binärer Suchbaum ADT Graph Exkurse: - Einfache Dateibehandlung - C++ Strings
3
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 3 Elementare Datenstrukturen Definition: Abstrakter Datentyp (ADT) ist ein Tripel (T, F, A), wobei T eine nicht leere Menge von Datenobjekten F eine Menge von Operationen, A eine nicht leere Menge von Axiomen, die die Bedeutung der Operationen erklären. Abstrakt? Datenobjekte brauchen keine konkrete Darstellung (Verallgemeinerung). Die Wirkung der Operationen wird beschrieben, nicht deren algorithmische Ausprägung. WAS, nicht WIE!
4
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 4 Beispiel: ADT bool F: Operationen true: bool false: bool not: bool bool and: bool x bool bool or: bool x bool bool A: Axiome not(false)= true not(true) = false and(false, false)= false and(false, true)= false and(true, false)= false and(true, true)= true or(x, y)= not(and(not(x), not(y))) Festlegung, welche Funktionen es gibt Festlegung, was die Funktionen bewirken Elementare Datenstrukturen
5
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 5 Wenn man ADT kennt, dann kann man ihn überall verwenden. Implementierung der Funktionen für Benutzer nicht von Bedeutung. Trennung von Spezifikation und Implementierung. Ermöglicht späteren Austausch der Implementierung, ohne dass sich der Ablauf anderer Programme, die ihn benutzen, ändert! Nur Operationen geben Zugriff auf Daten! Stichwort: Information Hiding! Eigenschaften Elementare Datenstrukturen
6
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 6 Lineare Datenstrukturen: Keller bzw. Stapel (engl. stack) Aufräumen: Kiste in Keller, oben auf Haufen. Etwas aus Keller holen: Zuerst Kiste, weil oben auf Haufen. Stapel Teller create: Keller push: Keller x T Keller pop: Keller Keller top: Keller T empty: Keller bool empty(create) = true empty(push(k, x))= false pop(push(k, x))= k top(push(k, x)) = x LIFO: Last in, first out. Elementare Datenstrukturen
7
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 7 Implementierung: (Version 1) const int maxSize = 100; struct Keller { T data[maxSize];// data array int sp;// stack pointer }; void create(Keller &k) { k.sp = -1; } bool empty(Keller k) { return k.sp == -1; } void pop(Keller &k) { k.sp--; } T top(Keller k) { return k.data[k.sp]; } void push(Keller &k, T x){ k.data[++k.sp] = x; } Probleme: Arraygrenzen! Elementare Datenstrukturen
8
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 8 Wann können Probleme auftreten? Bei pop, falls Keller leer ist: Stackpointer wird -2, anschließendes push versucht auf data[-1] zu schreiben Bei top, falls Keller leer ist: es wird undefinierter Wert von data[-1] zurückgegeben Bei push, falls Keller voll ist: es wird versucht auf data[maxsize] zu schreiben ) diese Fälle müssen abgefangen werden! Fehlermeldung! void error(char *info) { cerr << info << endl; exit(1); } gibt Fehlermeldung info aus und bricht das Programm durch exit(1) sofort ab und liefert den Wert des Arguments (hier: 1) an das Betriebssystem zurück Elementare Datenstrukturen
9
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 9 Implementierung: (Version 2, nur Änderungen und Zusätze bei Funktionen) const int maxSize = 100; struct Keller { T data[maxSize];// data array int sp;// stack pointer }; void pop(Keller &k) { if (!empty(k)) k.sp--; else error(leer); } T top(Keller k) { if (!empty(k)) return k.data[k.sp]; else error(leer); } void push(Keller &k, T x){ if (!full(k)) k.data[++k.sp] = x; else error(voll); } bool full(Keller k) { return k.sp == maxSize-1; } unverändert Elementare Datenstrukturen
10
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 10 Lineare Datenstrukturen: Schlange (engl. queue) Schlange an der Supermarktkasse: Wenn Einkauf fertig, dann hinten anstellen. Der nächste Kunde an der Kasse steht ganz vorne in der Schlange. create: Schlange enq: Schlange x T Schlange deq: Schlange Schlange front: Schlange T empty: Schlange bool empty(create) = true empty(enq(s, x))= false deq(enq(s, x))= empty(s) ? s : enq(deq(s), x) front(enq(s, x)) = empty(s) ? x : front(s) Eingehende Aufträge werden geparkt, und dann nach und nach in der Reihenfolge des Eingangs abgearbeitet. FIFO: First in, first out. Elementare Datenstrukturen
11
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 11 Implementierung: (Version 1) const int maxSize = 100; struct Schlange { T data[maxSize];// data array int ep;// end pointer }; void create(Schlange &s){ s.ep = -1; } bool empty(Schlange s) { return s.ep == -1; } T front(Schlange s) { return s.data[0]; } void enq(Schlange &s, T x) { s.data[++s.ep] = x; } Probleme: Arraygrenzen! void deq(Schlange &s) { for (int i=0; i<s.ep; i++) s.data[i] = s.data[i+1]; s.ep--; } Elementare Datenstrukturen
12
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 12 Implementierung: (Version 2, nur Änderungen und Zusätze bei Funktionen) const int maxSize = 100; struct Schlange { T data[maxSize];// data array int ep;// end pointer }; T front(Schlange s) { if (!empty(s)) return s.data[0]; error(leer); } void enq(Schlange &s, T x) { if (!full(s)) s.data[++s.ep] = x; else error(full); } void deq(Schlange &s) { if (empty(s)) error(leer); for (int i=0; i<s.ep; i++) s.data[i] = s.data[i+1]; s.ep--; } unverändert bool full(Schlange s) { return s.ep == maxSize-1; } Elementare Datenstrukturen
13
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 13 Laufzeit / Effizienz der Operation deq void deq(Schlange &s) { if (empty(s)) error(leer); for (int i=0; i < s.ep; i++) s.data[i] = s.data[i+1]; s.ep--; } s.ep = Anzahl Elemente in Schlange s.ep viele Datenverschiebungen Worst case: ( maxSize – 1) mal Benutzer des (abstrakten) Datentyps Schlange wird feststellen, dass 1.fast alle Operationen schnell sind, aber 2.die Operation deq vergleichsweise langsam ist. Elementare Datenstrukturen
14
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 14 Idee: Array zum Kreis machen; zusätzlich Anfang/Start markieren (s.sp) 0 1 2 3 7 6 5 4 s.sp s.ep Elementare Datenstrukturen
15
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 15 Implementierung: (Version 3) const int maxSize = 100; struct Schlange { T data[maxSize];// data array int sp;// start pointer int ep;// end pointer }; T front(Schlange s) { if (!empty(s)) return s.data[s.sp]; error(leer); } bool full(Schlange s) { int d = s.ep – s.sp; if ((d == -1) || (d == maxSize-1)) return; } void create(Schlange& s) { s.sp = 0; s.ep = maxSize; } bool empty(Schlange s) { return s.ep == maxSize; } Elementare Datenstrukturen
16
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 16 Implementierung: (Version 3) void enq(Schlange &s, T x) { if (!full(s)) { s.ep = (s.ep + 1) % maxSize; s.data[s.ep] = x; } else error(full); } void deq(Schlange &s) { if (empty(s)) error(leer); else if (s.sp == s.ep) create(s); else s.sp = (s.sp + 1) % maxSize; } Laufzeit: unabhängig von Größe der Schlange Laufzeit: unabhängig von Größe der Schlange Elementare Datenstrukturen
17
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 17 Unbefriedigend bei der Implementierung: Maximale festgelegte Größe des Kellers bzw. der Schlange! Liegt an der unterliegenden Datenstruktur Array: Array ist statisch, d.h. Größe wird zur Übersetzungszeit festgelegt und ist während der Laufzeit des Programms nicht veränderbar! Schön wären dynamische Datenstrukturen, d.h. Größe wird zur Übersetzungszeit nicht festgelegt und ist während der Laufzeit des Programms veränderbar! ) Dynamischer Speicher! (Stichwort: new / delete ) Elementare Datenstrukturen
18
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 18 Wiederholung: ADT Schlange Lineare Datenstrukturen: Schlange (engl. queue) create: Schlange enq: Schlange x T Schlange deq: Schlange Schlange front: Schlange T empty: Schlange bool create: erzeugt leere Schlange enq: hängt Element ans Ende der Schlange deq: entfernt Kopf der Schlange front: gibt im Kopf der Schlange gespeichertes Element zurück empty: prüft, ob Schlange leer ist Implementierung mit statischen Speicher ersetzen durch dynamischen Speicher
19
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 19 Wiederholung: Dynamischer Speicher Bauplan: Datentyp * Variable = new Datentyp;(Erzeugen) delete Variable;(Löschen) Bauplan für Arrays: Datentyp * Variable = new Datentyp [ Anzahl ] ;(Erzeugen) delete[] Variable;(Löschen) Achtung: Dynamisch erzeugte Objekte müssen auch wieder gelöscht werden! Keine automatische Speicherbereinigung!
20
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 20 Vorüberlegungen für ADT Schlange mit dynamischen Speicher: Wir können bei der Realisierung der Schlange statt statischen (Array) nun dynamischen Speicher verwenden … Ansatz: new int[oldsize+1] … bringt uns das weiter? Größe kann zwar zur Laufzeit angegeben werden, ist aber dann fixiert! Falls maximale Größe erreicht, könnte man 1.größeres Array anlegen 2.Arraywerte ins größere Array kopieren und 3.kleineres Array löschen. ineffizient! Elementare Datenstrukturen
21
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 21 Vorüberlegungen für ADT Schlange mit dynamischen Speicher: Objekt DatenfeldZeiger 0 zeigt auf Null (nichts): Nullpointer Start Ende Schlange Kopf +Schwanz der Schlange Elementare Datenstrukturen
22
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 22 Implementierung: (Version 4) struct Objekt { T data;// Datenfeld Objekt *tail;// Zeiger auf Schwanz der Schlange }; struct Schlange { Objekt *sp;// Start Objekt *ep;// Ende }; Schlange *create() { Schlange *s = new Schlange; s->ep = 0; return s; } bool empty(Schlange *s) { return s->ep == 0; } Speicher für Schlange allokieren Initialisierung! Rückgabe des Zeigers auf Schlange true falls s->ep == 0, sonst false Elementare Datenstrukturen
23
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 23 Implementierung: (Version 4) T front(Schlange *s) { if (!empty(s)) return (s->sp)->data; error(leer); } struct Objekt { T data;// Datenfeld Objekt *tail;// Zeiger auf Schwanz der Schlange }; struct Schlange { Objekt *sp;// Start Objekt *ep;// Ende }; void clear(Schlange *s) { while (!empty(s)) deq(s); } Was bedeutet s->sp->data ? identisch zu (*(*s).sp).data vorderstes Element entfernen bis Schlange leer Elementare Datenstrukturen
24
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 24 Implementierung: (Version 4) void enq(Schlange *s, T x) { Objekt *obj = new Objekt; obj->data = x; obj->tail = 0; if (empty(s)) s->sp = obj; else s->ep->tail = obj; s->ep = obj; } struct Objekt { T data;// Datenfeld Objekt *tail;// Zeiger auf Schwanz der Schlange }; struct Schlange { Objekt *sp;// Start Objekt *ep;// Ende }; neues Objekt anlegen (dyn. Speicher) neues Objekt initialisieren neues Objekt vorne, falls Schlange leer sonst hinten anhängen Aktualisierung des Endezeigers Elementare Datenstrukturen
25
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 25 Implementierung: (Version 4) void deq(Schlange *s) { if (empty(s))error(leer); Objekt *obj = s->sp; s->sp = s->sp->tail; if (s->sp == 0) s->ep = 0; delete obj; } struct Objekt { T data;// Datenfeld Objekt *tail;// Zeiger auf Schwanz der Schlange }; struct Schlange { Objekt *sp;// Start Objekt *ep;// Ende }; Zeiger auf zu löschendes Objekt retten Startzeiger auf 2. Element setzen falls kein 2. Element, dann Schlange leer ehemals 1. Element löschen Elementare Datenstrukturen
26
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 26 int main() { Schlange *s = create(); if (empty(s)) cout << "Schlange leer" << endl; for (int i = 0; i < 10; i++) enq(s, i); if (!empty(s)) cout << "Schlange nicht mehr leer" << endl; cout << "vorderstes Element: " << front(s) << endl; while (!empty(s)) { cout << front(s) << " "; deq(s); } cout << endl; if (empty(s)) cout << "Schlange jetzt leer" << endl; for (i = 0; i < 100; i++) enq(s,i); if (!empty(s)) cout << "Schlange nicht mehr leer" << endl; clear(s); if (empty(s)) cout << "Schlange wieder leer" << endl; } Testprogramm! Elementare Datenstrukturen
27
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 27 Elementare Datenstrukturen
28
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 28 ADT Liste (1. Version) struct Liste { T data; Liste *next; }; Liste wird nur durch einen Zeiger auf ihren Listenkopf repräsentiert Operationen: create: Liste empty: Liste bool append: T x Liste Listehängt am Ende an prepend: T x Liste Listevor Kopf einfügen clear: Liste is_elem: T x Liste boolist Element enthalten? Elementare Datenstrukturen
29
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 29 ADT Liste (1. Version) struct Liste { T data; Liste *next; }; Liste wird nur durch einen Zeiger auf ihren Listenkopf repräsentiert Liste *create() { return 0; } bool empty(Liste *liste) { return liste == 0; } void clear(Liste *liste) { if (empty(liste)) return; clear(liste->next); delete liste; } rekursives Löschen von hinten nach vorne Laufzeit: proportional zur Listenlänge Laufzeit: unabhängig von Listenlänge Elementare Datenstrukturen
30
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 30 ADT Liste (1. Version) struct Liste { T data; Liste *next; }; bool is_elem(T x, Liste *liste) { if (liste == 0) return false; if (liste->data == x) return true; return is_elem(x, liste->next); } Liste *prepend(T x, Liste *liste) { Liste *L = new Liste; L->data = x; L->next = liste; return L; } rekursiver Durchlauf von vorne nach hinten Laufzeit: proportional zur Listenlänge Laufzeit: unabhängig von Listenlänge Elementare Datenstrukturen
31
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 31 ADT Liste (1. Version) struct Liste { T data; Liste *next; }; Laufzeit: proportional zur Listenlänge iterativer Durchlauf von vorne nach hinten Liste *append(T x, Liste *liste) { Liste *tmp = liste; Liste *L = new Liste; L->data = x; L->next = 0; if (liste == 0) return L; while (liste->next != 0) liste = liste->next; liste->next = L; return tmp; } Zeiger auf Listenkopf retten Elementare Datenstrukturen
32
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 32 ADT Liste (1. Version) Zusammenfassung: 1.Laufzeit von clear proportional zur Listenlänge 2.Laufzeit von is_elem proportional zur Listenlänge 3.Laufzeit von append proportional zur Listenlänge kann nicht verbessert werden, weil ja jedes Element gelöscht werden muss unproblematisch, weil nur selten aufgerufen kann bei dieser Datenstruktur nicht verbessert werden später verbessert durch ADT BinärBaum kann durch Veränderung der Implementierung verbessert werden zusätzlicher Zeiger auf das Ende der Liste Elementare Datenstrukturen
33
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 33 ADT Liste (2. Version) struct Liste { Element *head; Element *foot; }; struct Element { T data; Element *next; }; Liste *create() { Liste *L = new Liste; L->head = L->foot = 0; return L; } bool empty(Liste *liste) { return liste->foot == 0; } Liste besteht aus 2 Zeigern: Zeiger auf Listenkopf (Anfang) Zeiger auf Listenfuß (Ende) Nutzdaten Zeiger auf nächstes Element Laufzeit: unabhängig von Listenlänge Elementare Datenstrukturen
34
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 34 ADT Liste (2. Version) struct Liste { Element *head; Element *foot; }; struct Element { T data; Element *next; }; bool is_elem(T x, Liste *liste) { if (empty(liste)) return false; Element *elem = liste->head; while (elem != 0) { if (elem->data == x) return true; elem = elem->next; } return false; } iterativer Durchlauf von vorne nach hinten Laufzeit: proportional zur Listenlänge keine Verbesserung (OK) Elementare Datenstrukturen
35
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 35 ADT Liste (2. Version) struct Liste { Element *head; Element *foot; }; struct Element { T data; Element *next; }; void clear(Liste *liste) { if (empty(liste)) return; Element *elem = liste->head; liste->head = elem->next; clear(liste); delete elem; } rekursives Löschen von hinten nach vorne Laufzeit: proportional zur Listenlänge keine Verbesserung (OK) Elementare Datenstrukturen
36
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 36 ADT Liste (2. Version) struct Liste { Element *head; Element *foot; }; struct Element { T data; Element *next; }; Liste *prepend(T x, Liste *liste) { Element *elem = new Element; elem->data = x; elem->next = liste->head; if (empty(liste)) liste->foot = elem; liste->head = elem; return liste; } Laufzeit: unabhängig von Listenlänge Elementare Datenstrukturen
37
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 37 ADT Liste (2. Version) struct Liste { Element *head; Element *foot; }; struct Element { T data; Element *next; }; Liste *append(T x, Liste *liste) { Element *elem = new Element; elem->data = x; elem->next = 0; if (empty(liste)) liste->head = elem; else liste->foot->next = elem; liste->foot = elem; } Laufzeit: unabhängig von Listenlänge Verbesserung! Elementare Datenstrukturen
38
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 38 ADT Liste (2. Version) Zusammenfassung: 1.Laufzeit von clear proportional zur Listenlänge 2.Laufzeit von is_elem proportional zur Listenlänge 3.Laufzeit von append unabhängig von Listenlänge kann nicht verbessert werden, weil ja jedes Element gelöscht werden muss unproblematisch, weil nur selten aufgerufen kann bei dieser Datenstruktur nicht verbessert werden verbessern wir gleich durch ADT BinärBaum war proportional zur Listenlänge in 1. Version Verbesserung erzielt durch Veränderung der Implementierung Elementare Datenstrukturen
39
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 39 ADT Binäre Bäume Vorbemerkungen: Zahlenfolge (z. B. 17, 4, 36, 2, 8, 19, 40, 6, 7, 37) soll gespeichert werden, um später darin suchen zu können Man könnte sich eine Menge A vorstellen mit Anfrage: Ist 40 A ? Mögliche Lösung: Zahlen in einer Liste speichern und nach 40 suchen … … aber: nicht effizient, weil im schlechtesten Fall alle Elemente überprüft werden müssen! Bessere Lösungen? Elementare Datenstrukturen
40
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 40 ADT Binäre Bäume Beispiel: Zahlenfolge 17, 4, 36, 2, 8, 19, 40, 6, 7, 37 17 436 281940 637 7 kleiner: nach links größer: nach rechts z.B. Suche, ob 42 enthalten benötigt nur 3 Vergleiche bis zur Entscheidung false Elementare Datenstrukturen
41
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 41 ADT Binäre Bäume Beispiel: Zahlenfolge 17, 4, 36, 2, 8, 19, 40, 6, 7, 37 17 436 281940 637 7 kleiner: nach links größer: nach rechts z.B. Suche, ob 42 enthalten benötigt nur 3 Vergleiche bis zur Entscheidung false Elementare Datenstrukturen
42
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 42 ADT Binäre Bäume: Terminologie LR linker Unterbaum rechter Unterbaum Wurzel Blätter keine Wurzel und kein Blatt innerer Knoten Elementare Datenstrukturen
43
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 43 ADT Binäre Bäume: Datenstruktur struct BinTree { T data;// Nutzdaten BinTree *lTree, *rTree;// linker und rechter Unterbaum }; Falls ein Unterbaum nicht existiert, dann zeigt der Zeiger auf 0. bool IsElement(int key, BinTree *tree) { if (tree == 0) return false; if (tree->data == key) return true; if (tree->data rTree); return IsElement(key, tree->lTree); } Elementare Datenstrukturen
44
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 44 ADT Binäre Bäume: Einfügen BinTree *Insert(int key, BinTree *tree) { if (tree == 0) { BinTree *b = new BinTree; b->data = key; b->lTree = b->rTree = 0; return b; } else { if (tree->data rTree = Insert(key, tree->rTree); else if (tree->data > key) tree->lTree = Insert(key, tree->lTree); return tree; } } Elementare Datenstrukturen
45
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 45 ADT Binäre Bäume: Aufräumen void Clear(BinTree *tree) { if (tree == 0) return;// Rekursionsabbruch Clear(tree->lTree);// linken Unterbaum löschen Clear(tree->rTree);// rechten Unterbaum löschen delete tree;// aktuellen Knoten löschen } u.s.w. 1 2 3 45 6 7 89 10 11 1213 14 15 16 17 18 19 20 Elementare Datenstrukturen
46
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 46 ADT Binäre Bäume Höhe := Länge des längsten Pfades von der Wurzel zu einem Blatt. Höhe(leerer Baum) = 0 Höhe(nicht leerer Baum) = 1 + max { Höhe(linker U-Baum), Höhe(rechter U-Baum) } (U-Baum = Unterbaum) Anmerkung: rekursive Definition! Elementare Datenstrukturen
47
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 47 ADT Binäre Bäume 1 2 3 4 Auf Ebene k können jeweils zwischen 1 und 2 k-1 Elemente gespeichert werden. In einem Baum der Höhe h können also zwischen h und Elemente gespeichert werden! Elementare Datenstrukturen
48
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 48 ADT Binäre Bäume 1 2 3 4 Ein vollständiger Baum der Höhe h besitzt 2 h – 1 Knoten. Man braucht maximal h Vergleiche, um Element (ggf. nicht) zu finden. Bei n = 2 h – 1 Elementen braucht man log 2 (n) < h Vergleiche! Ein degenerierter Baum der Höhe h besitzt h Knoten (= lineare Liste). Man braucht maximal h Vergleiche, um Element (ggf. nicht) zu finden. Bei n = h braucht man also n Vergleiche! Elementare Datenstrukturen
49
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 49 Exkurs: Einfache Dateibehandlung Datei := speichert Daten in linearer Anordnung Zwei Typen: ASCII-Dateien - sind mit Editor les- und schreibbar - Dateiendung (suffix oder extension) meist.txt oder.asc - betriebssystem-spezifische Übersetzung von Zeichen bei Datentransfer zwischen Programm und externem Speicher Binär-Dateien - werden byteweise beschrieben und gelesen - lesen / schreiben mit Editor ist keine gute Idee - schnellerer Datentransfer, da keine Zeichenübersetzung
50
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 50 Hier: einfache Dateibehandlung! Dateien können gelesen oder beschrieben werden. Vor dem ersten Lesen oder Schreiben muss Datei geöffnet werden. Man kann prüfen, ob das Öffnen funktioniert hat. Nach dem letzten Lesen oder Schreiben muss Datei geschlossen werden. Bei zu lesenden Dateien kann gefragt werden, ob Ende der Datei erreicht ist. Beim Öffnen einer zu schreibenden Datei wird vorheriger Inhalt gelöscht! Man kann noch viel mehr machen … wir benötigen: #include bzw. Exkurs: Einfache Dateibehandlung
51
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 51 Eingabe-Datei = input file ifstream Quelldatei; DatentypBezeichner Ausgabe-Datei = output file ofstream Zieldatei; DatentypBezeichner Öffnen der Datei: Quelldatei.open(dateiName); ist Kurzform von Quelldatei.open(dateiName, modus); wobei fehlender modus bedeutet: ASCII-Datei, Eingabedatei (weil ifstream) Öffnen der Datei: Zieldatei.open(dateiName); ist Kurzform von Quelldatei.open(dateiName, modus); wobei fehlender modus bedeutet: ASCII-Datei, Ausgabedatei (weil ofstream) Exkurs: Einfache Dateibehandlung
52
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 52 modus: ios::binary binäre Datei ios::in öffnet für Eingabe (implizit bei ifstream) ios::out öffnet für Ausgabe (implizit bei ofstream) ios::app hängt Daten am Dateiende an ios::nocreate wenn Datei existiert, dann nicht anlegen Warnung: teilweise Compiler-abhängig ( nocreate fehlt in MS VS 2003, dafür trunc ) Man kann diese Schalter / Flags miteinander kombinieren via: ios::binary | ios::app (öffnet als binäre Datei und hängt Daten an) Exkurs: Einfache Dateibehandlung
53
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 53 Datei öffnen file.open(fileName) bzw. file.open(fileName, modus) falls Öffnen fehlschlägt, wird Nullpointer zurückgegeben Datei schließen file.close() sorgt für definierten Zustand der Datei auf Dateisystem; bei nicht geschlossenen Dateien droht Datenverlust! Ende erreicht? ja falls file.eof() == true Lesen (von ifstream) file.get(c); liest ein Zeichen file >> x; liest verschiedene Typen Schreiben (von ofstream) file.put(c); schreibt ein Zeichen file << x; schreibt verschiedene Typen Exkurs: Einfache Dateibehandlung
54
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 54 Merke: 1.Auf eine geöffnete Datei darf immer nur einer zugreifen. 2.Eine geöffnete Datei belegt Ressourcen des Betriebssystems. Deshalb Datei nicht länger als nötig geöffnet halten. 3.Eine geöffnete Datei unbekannter Länge kann solange gelesen werden, bis das Ende-Bit (end of file, EOF) gesetzt wird. 4.Der Versuch, eine nicht vorhandene Datei zu öffnen (zum Lesen) oder eine schreibgeschützte Datei zu öffnen (zum Schreiben), führt zu einem Nullpointer. Das muss überprüft werden, sonst Absturz bei weiterer Verwendung! 5.Dateieingabe und -ausgabe (input/output, I/O) ist sehr langsam im Vergleich zu den Rechenoperationen. I/O Operationen minimieren. The fastest I/O is no I/O. Nils-Peter Nelson, Bell Labs Exkurs: Einfache Dateibehandlung
55
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 55 #include using namespace std; int main() {// zeichenweise kopieren ifstream Quelldatei; ofstream Zieldatei; Quelldatei.open("quelle.txt"); if (!Quelldatei.is_open()) { cerr << "konnte Datei nicht zum Lesen öffnen\n"; exit(1); } Zieldatei.open("ziel.txt"); if (!Zieldatei.is_open()) { cerr << "konnte Datei nicht zum Schreiben öffnen\n"; exit(1); } Exkurs: Einfache Dateibehandlung
56
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 56 while (!Quelldatei.eof()) { char c; Quelldatei.get(c); Zieldatei.put(c); } Quelldatei.close(); Zieldatei.close(); } offene Datei Startaktuelle Position eof() == true Exkurs: Einfache Dateibehandlung
57
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 57 Exkurs: C++ Strings Bisher: Zeichenketten wie char str[20]; Relikt aus C-Programmierung! bei größeren Programmen mühevoll, lästig, … … und insgesamt fehlerträchtig! Jetzt: Zeichenketten aus C++ sehr angenehm zu verwenden (keine 0 am Ende, variable Größe, …) eingebaute (umfangreiche) Funktionalität wie benötigen: #include und using namespace std;
58
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 58 string s1;// leerer String string s2 = "xyz";// initialisieren mit C-String string s3 = s2;// vollständige Kopie! string s4("abc");// initialisieren mit C-String string s5(s4);// initialisieren mit C++-String string s6(10, *);// ergibt String aus 10 mal * string s7(1x);// initialisieren mit einem char string sx(x);// FEHLER! string s8("");// leerer String Datendefinition / Initialisierung Exkurs: C++ Strings
59
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 59 const char *Cstr = s2.c_str(); Eingebaute Funktionen Konvertierung C++-String nach C-String via c_str() cout << s2.length(); Stringlänge length() substr(), replace(), erase(), … Index von Teilstring finden int pos = s2.find(yz); Strings addieren s1 = s2 + s3; s4 = s2 + hello; s5 += s4; Strings vergleichen if (s1 == s2) s3 += s2; if (s3 < s8) flag = true; Exkurs: C++ Strings
60
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 60 ADT Binäre Bäume: Anwendung Aufgabe: Gegeben sei eine Textdatei. Häufigkeiten der vorkommenden Worte feststellen. Alphabetisch sortiert ausgeben. Strategische Überlegungen: Lesen aus Textdatei ifstream benutzen! Sortierte Ausgabe Binärbaum: schnelles Einfügen, sortiert von selbst Worte vergleichen C++ Strings verwenden! Programmskizze: Jeweils ein Wort aus Datei lesen und in Binärbaum eintragen. Falls Wort schon vorhanden, dann Zähler erhöhen. Wenn alle Wörter eingetragen, Ausgabe (Wort, Anzahl) via Inorder-Durchlauf. Elementare Datenstrukturen
61
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 61 struct BinBaum { string wort; int anzahl; BinBaum *links, *rechts; }; BinBaum *Einlesen(string &dateiname) { ifstream quelle; quelle.open(dateiname.c_str()); if (!quelle.is_open()) return 0; string s; BinBaum *b = 0; while (!quelle.eof()) { quelle >> s; b = Einfuegen(s, b); } quelle.close(); return b; } Datei öffnen Worte einzeln auslesen und im Baum einfügen Datei schließen + Zeiger auf Baum zurückgeben gelesenes Wort wie oft gelesen? Elementare Datenstrukturen
62
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 62 BinBaum *Einfuegen(string &s, BinBaum *b) { if (b == 0) { b = new BinBaum; b->wort = s; b->anzahl = 1; b->links = b->rechts = 0; return b; } if (b->wort rechts = Einfuegen(s, b->rechts); else if (b->wort > s) b->links = Einfuegen(s, b->links); else b->anzahl++; return b; } Elementare Datenstrukturen
63
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 63 void Ausgabe(BinBaum *b) { if (b == 0) return; Ausgabe(b->links); cout anzahl wort.c_str() << endl; Ausgabe(b->rechts); } Dies ist die Inorder-Ausgabe. Präorder: cout …; Ausgabe(…); Ausgabe(…); Postorder: Ausgabe(…); Ausgabe(…); cout …; Elementare Datenstrukturen
64
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 64 #include using namespace std; int main() { string s("quelle.txt"); BinBaum *b = Einlesen(s); Ausgabe(b); return 0; } Hauptprogramm: Elementare Datenstrukturen
65
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 65 Durchlaufstrategien: Tiefensuche (depth-first search, DFS) - Präorder Vor (prä) Abstieg in Unterbäume die Knotenbehandlung durchführen - Postorder Nach (post) Abstieg in bzw. Rückkehr aus Unterbäumen die Knotenbehandlung durchführen - Inorder Zwischen zwei Abstiegen Knotenbehandlung durchführen z.B. Ausdruck des Knotenwertes Breitensuche (breadth-first search, BFS; auch: level search) auf jeder Ebene des Baumes werden Knoten abgearbeitet, bevor in die Tiefe gegangen wird Elementare Datenstrukturen
66
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 66 Breitensuche Beispiel: eingegebene Zahlenfolge 17, 4, 36, 2, 8, 40, 19, 6, 7, 37 17 436 281940 637 7 Ausgabe: 17, 4, 36, 2, 8, 19, 40, 6, 37, 7 Implementierung: Praktikum! Elementare Datenstrukturen
67
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 67 ADT Graph Verallgemeinerung von (binären) Bäumen Wichtige Struktur in der Informatik Zahlreiche Anwendungsmöglichkeiten - Modellierung von Telefonnetzen, Versorgungsnetzwerken, Straßenverkehr, … - Layout-Fragen bei elektrischen Schaltungen - Darstellung sozialer Beziehungen - etc. Viele Probleme lassen sich als Graphenprobleme verkleiden und dann mit Graphalgorithmen lösen! Elementare Datenstrukturen
68
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 68 Definition EIn Graph G = (V, E) besteht aus einer Menge von Knoten V (vertex, pl. vertices) und einer Menge von Kanten E (edge, pl. edges) mit E V x V. 4 6 5 1 2 3 7 Eine Kante (u, v) heißt Schlinge (loop), wenn u = v. Der Grad (degree) eines Knotens v V ist die Anzahl der zu ihm inzidenten Kanten: deg(v) = | { (a, b) E : a = v oder b = v } |. Maxgrad von G ist (G) = max { deg(v) : v V } Mingrad von G ist (G) = min { deg(v) : v V } (G) = 6 (G)= 3 Schlinge Elementare Datenstrukturen
69
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 69 Definition Für v i V heißt (v 0, v 1, v 2, …, v k ) ein Weg oder Pfad in G, wenn (v i,v i+1 ) E für alle i = 0, 1, …, k-1. Die Länge eines Pfades ist die Anzahl seiner Kanten. Ein Pfad (v 0, v 1, v 2, …, v k ) mit v 0 = v k wird Kreis genannt. Distanz dist(u, v) von zwei Knoten ist die Länge des kürzesten Pfades von u nach v. Durchmesser diam(G) eines Graphes G ist das Maximum über alle Distanzen: diam(G) = max { dist(u, v) : (u, v) V x V }. Graph ist zusammenhängend, wenn 8 u, v V mit u v einen Pfad gibt. G heißt Baum gdw. G zusammenhängend und kreisfrei. Elementare Datenstrukturen
70
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 70 Darstellung im Computer Adjazenzmatrix A mit a ij = 1 falls (v i,v j ) E 0 sonst Problem: Da | E | | V | 2 = n 2 ist Datenstruktur ineffizient (viele Nullen) wenn | E | verschwindend klein. Adjazenzlisten: Für jeden Knoten v eine (Nachbarschafts-) Liste L(v) mit L(v) = { u V : (v, u) E } Elementare Datenstrukturen
71
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 71 Beispiel e d a b c L(a) = ( b, e ) L(b) = ( a, c, d ) L(c) = ( b, d ) L(d) = ( b, c, e ) L(e) = ( a, d ) Adjazenzlisten abcde a01001 b10110 c01010 d01101 e10010 Adjazenzmatrix ADT Liste Array[][] Elementare Datenstrukturen
72
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 72 Graph *createGraph(uint NoOfNodes); void addEdge(Graph *graph, uint Node1, uint Node2); void deleteEdge(Graph *graph, uint Node1, uint Node2); bool hasEdge(Graph *graph, uint Node1, uint Node2); uint noOfEdges(); uint noOfNodes(); void printGraph(); Mögliche Funktionalität typedef unsigned int uint;typedef Datentyp TypName; Elementare Datenstrukturen
73
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 73 struct Graph { uint NoOfNodes; List **AdjList; }; Graph *createGraph(uint NoOfNodes) { Graph *graph = new Graph; graph->NoOfNodes = NoOfNodes; graph->AdjList = new List*[NoOfNodes]; for (uint i = 0; i < NoOfNodes; i++) graph->AdjList[i] = create(); return graph; } // Zeiger auf Zeiger auf Listen AdjList List Speicher reservieren initialisieren! Elementare Datenstrukturen
74
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 74 struct Graph { uint NoOfNodes; List **AdjList; }; // Zeiger auf Zeiger auf Listen AdjList List uint noOfNodes(Graph *graph) { return graph == 0 ? 0 : graph->NoOfNodes; } uint noOfEdges(Graph *graph) { if (noOfNodes(graph) == 0) return 0; unsigned int cnt = 0, i; for (i = 0; i < noOfNodes(graph); i++) cnt += size(graph->AdjList[i]); return cnt / 2; } Ineffizient! Falls häufig benutzt, dann besser Zähler NoOfEdges im struct Graph Elementare Datenstrukturen
75
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 75 struct Graph { uint NoOfNodes; List **AdjList; }; // Zeiger auf Zeiger auf Listen AdjList List bool hasEdge(Graph *graph, uint Node1, uint Node2) { if (graph == 0) error("no graph"); uint n = noOfNodes(graph); if (Node1 >= n || Node2 >= n) error("invalid node"); return is_elem(Node2, graph->AdjList[Node1]); } void addEdge(Graph *graph, uint Node1, uint Node2) { if (!hasEdge(graph, Node1, Node2)) { append(Node2, graph->AdjList[Node1]); append(Node1, graph->AdjList[Node2]); } } Elementare Datenstrukturen
76
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 76 struct Graph { uint NoOfNodes; List **AdjList; }; // Zeiger auf Zeiger auf Listen AdjList List void printGraph(Graph *graph) { if (noOfNodes(graph) == 0) return; unsigned int i, n = noOfNodes(graph); for (i = 0; i < n; i++){ cout << i << ": "; printList(graph->AdjList[i]); } Elementare Datenstrukturen
77
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 77 struct Graph { uint NoOfNodes; List **AdjList; }; // Zeiger auf Zeiger auf Listen AdjList List Graph *clearGraph(Graph *graph) { if (graph == 0) return 0; unsigned int cnt = 0, i; for (i = 0; i < noOfNodes(graph); i++) clearList(graph->AdjList[i]); delete[] graph->AdjList; delete graph; return 0; } Elementare Datenstrukturen
78
Kapitel 8 G. Rudolph: Einführung in die Programmierung WS 2008/09 78 int main() { Graph *graph = createGraph(7); addEdge(graph, 0, 2); addEdge(graph, 1, 2); addEdge(graph, 3, 2); addEdge(graph, 2, 4); addEdge(graph, 4, 6); addEdge(graph, 6, 5); addEdge(graph, 5, 2); addEdge(graph, 2, 3); addEdge(graph, 3, 0); addEdge(graph, 6, 3); addEdge(graph, 0, 1); cout << "nodes: " << noOfNodes(graph) << endl; cout << "edges: " << noOfEdges(graph) << endl; printGraph(graph); graph = clearGraph(graph); cout << "nodes: " << noOfNodes(graph) << endl; cout << "edges: " << noOfEdges(graph) << endl; return 0; } 0 16 25 34 nodes: 7 edges: 11 0: 2 1 1: 2 2: 4 3 3: 2 0 4: 6 5: 2 6: 5 3 nodes: 0 edges: 0 Elementare Datenstrukturen
Ähnliche Präsentationen
© 2024 SlidePlayer.org Inc.
All rights reserved.