Algorithmen und Datenstrukturen (für ET/IT) Wintersemester 2012/13 Dr. Tobias Lasser Computer Aided Medical Procedures Technische Universität München Organisatorisches Nächste Woche keine Vorlesung! Es finden nächste Woche keine Vorlesungen statt! • Mittwoch, 31. Oktober 2012: FVV (Fachschaftsvollversammlung) • Donnerstag, 1. November 2012: Feiertag (Allerheiligen) Der Übungsbetrieb (Zentralübung, Montag 29. Oktober 2012, sowie die Fragestunden, Freitag, 2. November 2012) findet statt! 2 Wiederholung letzte Vorlesung Ganze Zahlen in C, C++ Ganze Zahlen in C, C++ • 8 Bits: unsigned char {0, . . . , 255} signed char {−128, . . . , 127} • 16 Bits: unsigned short {0, . . . , 65535} signed short {−32768, . . . , 32767} • 32 Bits: unsigned long {0, . . . , 232 − 1} signed long {−232 , . . . , 232 − 1} • 64 Bits: unsigned long long {0, . . . , 264 − 1} signed long long {−264 , . . . , 264 − 1} • signed kann weggelassen werden (ausser bei char!) • unsigned int und signed int sind je nach System 16, 32 oder 64 Bit 11 3 Wiederholung letzte Vorlesung Floating Point Zahlen II 1 Bit 11 Bit 52 Bit 64 Bit double 1 Bit 8 Bit 23 Bit 32 Bit float V Exponent E Mantisse M Ganze Zahlen in C, C++ • wissenschaftliche Darstellung mit Basis 2 Ganze Zahlen in C, C++ f = (−1)V · (1 + M) · 2E −bias • 8 Bits: unsigned char {0, . . . , 255} signed• char {−128,Bit . . .V, 127} Vorzeichen • 16 Bits: unsigned short M {0,hat . . . immer , 65535} • Mantisse die Form 1.abc, also wird erste Stelle signed weggelassen short {−32768, . . . , 32767} ( hidden bit“) ” • 32 Bits: unsigned long {0, . . , 232 − 1} • Exponent E .wird vorzeichenlos abgespeichert, verschoben um signed bias long {−232 , . . . , 232 − 1} 64 127, bei 32long bit float: • 64 Bits: unsigned •long {0, . .bias . , 2= − 1}bei 64 bit float: bias = 1023 signed long long {−264 , . . . , 264 − 1} 15 • signed kann weggelassen werden (ausser bei char!) • unsigned int und signed int sind je nach System 16, 32 oder 64 Bit 11 3 Wiederholung letzte Vorlesung Logische Werte und Verknüpfungen Grundrechenarten“ mit logischen Werten: ” Floating Point • Konjunktion: ∧ : B × B → B • ähnlich zu Multiplikation bei Zahlen Zahlen• IIauch bezeichnet als UND bzw. AND 1 Bit 11 Bit 1 Bit 8 Bit • V Ganze Zahlen in C, C++ 52 Bit Disjunktion: ∨ : 23BBit× B → B 64 Bit double 32 Bit float Exponent E • ähnlich zu Mantisse M bei Zahlen Addition • auch bezeichnet als ODER bzw. OR • wissenschaftliche Darstellung mit Basis 2 • Negation: ¬ : B → B V f •= auch (−1)bezeichnet · (1 + M) 2E −bias bzw. NOT als· NICHT • 8 Bits: unsigned char {0, . . . , 255} Wahrheitstabelle: a 0 0 1 1 b 0 1 0 1 a∧b 0 0 0 1 a 0 0 1 1 b 0 1 0 1 a∨b 0 1 1 1 a 0 1 ¬a 1 0 Ganze Zahlen in C, C++ signed• char {−128,Bit . . .V, 127} Vorzeichen • 16 Bits: unsigned short M {0,hat . . . immer , 65535} • Mantisse die Form 1.abc, also wird erste Stelle signed weggelassen short {−32768, . . . , 32767} ( hidden bit“) ” 21 • 32 Bits: unsigned long {0, . . , 232 − 1} • Exponent E .wird vorzeichenlos abgespeichert, verschoben um signed bias long {−232 , . . . , 232 − 1} 64 127, bei 32long bit float: • 64 Bits: unsigned •long {0, . .bias . , 2= − 1}bei 64 bit float: bias = 1023 signed long long {−264 , . . . , 264 − 1} 15 • signed kann weggelassen werden (ausser bei char!) • unsigned int und signed int sind je nach System 16, 32 oder 64 Bit 11 3 Wiederholung letzte Vorlesung Logische Werte und Verknüpfungen Grundrechenarten“ mit logischen Werten: ” b 0 1 0 1 a∧b 0 0 0 1 • logische Variablen: bool a,b; a b Disjunktion: ∨ : 23BBit× B → B 32 Bit float • logische Werte: true und false 0 0 Exponent E • ähnlich zu Mantisse M bei Zahlen Addition 1 0 1 a∨b 0 1 1 1 a 0 1 ¬a 1 0 Floating Point 1 Bit 1 Bit V Ganze Zahlen in C, C++ • Konjunktion: ∧ : B × B → B • ähnlich zu Multiplikation bei Zahlen Zahlen• IIauch Logische Ausdrücke in C, bezeichnet als UND bzw. AND Wahrheitstabelle: 11 Bit 52 Bit 64 Bit double a 0 C++ 0 1 1 8 Bit • • auch bezeichnet als ODER bzw. !a OR • NOT Operator: • AND Operator: a && b • wissenschaftliche Darstellung mit Basis 2 • OR Operator: a || b • Negation: ¬ : B → B V f •= auch (−1)bezeichnet · (1 + M) 2E −bias bzw. NOT als· NICHT • 8 Bits: unsigned char {0, . . . , 255} Beispiele: 0 1 1 Ganze Zahlen in C, C++ signed• char {−128,Bit . . .V, 127} Vorzeichen • ( (2 == 2) && (3 > 1) ) • 16 Bits: unsigned short M {0,hat . . . immer , 65535} ergibt (true &&erste false), • Mantisse die Form 1.abc, also wird Stellealso false signed weggelassen short {−32768, . . . , 32767} ( hidden bit“) • ( !(2 == 2) || (3 < 1) ) ” 32 • 32 Bits: unsigned long {0, . . , 2vorzeichenlos − 1} ergibt (falseverschoben || true), • Exponent E .wird abgespeichert, umalso true 32 21 32 signed bias long {−2 , . . . , 2 − 1} • Kurzform für !(2 == 2) ist (2 != 2) 64 127, bei 32long bit float: • 64 Bits: unsigned •long {0, . .bias . , 2= − 1}bei 64 bit float: bias = 1023 signed long long {−264 , . . . , 264 − 1} 15 27 • signed kann weggelassen werden (ausser bei char!) • unsigned int und signed int sind je nach System 16, 32 oder 64 Bit 11 3 Programm heute 1 Einführung 2 Mathematische Grundlagen Mengen Abbildungen Zahldarstellung Boolesche Logik 3 Elementare Datenstrukturen Zeichenfolgen Felder 4 Definition Datenstruktur Definition Datenstruktur (nach Prof. Eckert) Eine Datenstruktur ist eine • logische Anordnung von Datenobjekten, • die Informationen repräsentieren, • den Zugriff auf die repräsentierte Information über Operationen auf Daten ermöglichen und • die Information verwalten. Zwei Hauptbestandteile: • Datenobjekte • z.B. definiert über primitive Datentypen • Operationen auf den Objekten • z.B. definiert als Funktionen 5 Primitive Datentypen in C, C++ • Natürliche Zahlen, z.B. unsigned short, unsigned long • Wertebereich: bei n Bit von 0 bis 2n − 1 • Operationen: +, -, *, /, %, <, ==, !=, > • Ganze Zahlen, z.B. int, long • Wertebereich: bei n Bit von −2n−1 bis 2n−1 − 1 • Operationen: +, -, *, /, %, <, ==, !=, > • Floating Point Zahlen, z.B. double, float • Wertebereich: abhängig von Größe • Operationen: +, -, *, /, <, ==, !=, > • Logische Werte, bool • Wertebereich: true, false • Operationen: &&, ||, !, ==, != Was ist mit Zeichen und Zeichenfolgen? 6 Bits und Bytes Bit 7 Bit 0 1 Byte = 8 Bit Bytes als Maßeinheit für Speichergrössen: • 210 Bytes = 1024 Bytes = 1 kB, ein kilo Byte (großes B) • 220 Bytes = 1 MB, ein Mega Byte • 230 Bytes = 1 GB, ein Giga Byte • 240 Bytes = 1 TB, ein Tera Byte • 250 Bytes = 1 PB, ein Peta Byte • 260 Bytes = 1 EB, ein Exa Byte 7 Bits und Bytes Bit 7 Bit 0 1 Byte = 8 Bit Bytes als Maßeinheit für Speichergrössen: • 210 Bytes = 1024 Bytes = 1 kB, ein kilo Byte (großes B) • 220 Bytes = 1 MB, ein Mega Byte • 230 Bytes = 1 GB, ein Giga Byte • 240 Bytes = 1 TB, ein Tera Byte • 250 Bytes = 1 PB, ein Peta Byte • 260 Bytes = 1 EB, ein Exa Byte Hinweis: auch Bits werden als Maßangabe verwendet, z.B. 16 Mbit oder 16 Mb (kleines b). 7 Bytes und ASCII Interpretation eines Bytes als Zeichen (anstatt Zahlen) −→ z.B. ASCII Code 7 Bit ASCII Code: Code ..0 ..1 0.. 1.. 2.. 3.. 4.. 5.. 6.. 7.. ..2 ..3 ..4 ..5 ..6 ..7 nul soh stx etx eot enq ack dle dc1 dc2 dc3 dc4 nak syn sp ! “ # $ % & 0 1 2 3 4 5 6 @ A B C D E F P Q R S T U V ‘ a b c d e f p q r s t u v ..8 ..9 ..A ..B ..C ..D ..E ..F bel bs ht lf vt etb can em sub esc ’ ( ) * + 7 8 9 : ; G H I J K W X Y Z [ g h i j k w x y z { ff fs , < L \ l ! cr so si gs rs us . / = > ? M N O ] ˆ m n o } ˜ del 8 ASCII Erweiterungen, Unicode • ASCII verwendet nur 7 Bit von einem Byte • enthält z.B. keine Umlaute (ä, ö, ü) oder Akzente (é, ç) 9 ASCII Erweiterungen, Unicode • ASCII verwendet nur 7 Bit von einem Byte • enthält z.B. keine Umlaute (ä, ö, ü) oder Akzente (é, ç) • es gibt verschiedene Erweiterungen von ASCII auf 8 Bit • in Europa ist ISO Latin-1 verbreitet (ISO Norm 8859-1) • belegt die Codes von 128-255 (bzw. 80-FF in hex) 9 ASCII Erweiterungen, Unicode • ASCII verwendet nur 7 Bit von einem Byte • enthält z.B. keine Umlaute (ä, ö, ü) oder Akzente (é, ç) • es gibt verschiedene Erweiterungen von ASCII auf 8 Bit • in Europa ist ISO Latin-1 verbreitet (ISO Norm 8859-1) • belegt die Codes von 128-255 (bzw. 80-FF in hex) • Unicode wurde als 16 Bit Codierung eingeführt • erste 128 Zeichen stimmen mit ASCII überein • die nächsten 128 Zeichen mit ISO Latin-1 • danach z.B. kyrillische, arabische, japanische Schriftzeichen 9 ASCII Erweiterungen, Unicode • ASCII verwendet nur 7 Bit von einem Byte • enthält z.B. keine Umlaute (ä, ö, ü) oder Akzente (é, ç) • es gibt verschiedene Erweiterungen von ASCII auf 8 Bit • in Europa ist ISO Latin-1 verbreitet (ISO Norm 8859-1) • belegt die Codes von 128-255 (bzw. 80-FF in hex) • Unicode wurde als 16 Bit Codierung eingeführt • erste 128 Zeichen stimmen mit ASCII überein • die nächsten 128 Zeichen mit ISO Latin-1 • danach z.B. kyrillische, arabische, japanische Schriftzeichen • UTF-8 ist eine Mehrbyte-Codierung von Unicode (1-6 Bytes) • Code-Länge wird durch die ersten Bits codiert 9 Zeichen und Strings Repräsentation eines ASCII Zeichens in C, C++: char • Zeichen-Literale in einfachen Anführungszeichen Beispiele: ’A’, ’u’, ’D’ char zeichen = ’A ’; • Vorsicht bei nicht-ASCII Zeichen! 10 Zeichen und Strings Repräsentation eines ASCII Zeichens in C, C++: char • Zeichen-Literale in einfachen Anführungszeichen Beispiele: ’A’, ’u’, ’D’ char zeichen = ’A ’; • Vorsicht bei nicht-ASCII Zeichen! Repräsentation einer Zeichenfolge? (Englisch: String) • String-Literale in doppelten Anführungszeichen Beispiel: “AuD“ • in C gespeichert als Folge von Zeichen, terminiert durch ’\0’ 'A' 'u' 'D' '\0' 10 Strings in C Strings in C: • dargestellt als Array von chars fester Länge: char test_string [8]; • C Arrays werden indiziert von 0! (hier also von 0 bis 7) • immer genug Platz für terminierendes ’\0’ einplanen! 11 Strings in C Strings in C: • dargestellt als Array von chars fester Länge: char test_string [8]; • C Arrays werden indiziert von 0! (hier also von 0 bis 7) • immer genug Platz für terminierendes ’\0’ einplanen! • dargestellt als Array von chars dynamischer Länge (mittels Pointer): char * test_string ; • Speicher muss explizit über malloc/free angefordert/freigegeben werden! • Größe des Arrays komplett in eigener Verantwortung! • sehr fehleranfällig, bitte vermeiden wenn möglich! 11 Programm: Strings in C # include < stdio .h > # include < string .h > int main () { char test_string [5] = " test " ; // Laenge ist 4+1 printf ( " test_string : ! % s \ n " , test_string ); char AuD_string [4]; AuD_string [0] = ’A ’; AuD_string [1] = ’u ’; AuD_string [2] = ’D ’; AuD_string [3] = ’ \0 ’; // terminierende 0 printf ( " AuD_string : ! % s \ n " , AuD_string ); printf ( " Laenge : ! % lu \ n " , strlen ( AuD_string ) ); return 0; } 12 Strings in C - Operationen Operationen auf C Strings: definiert in string.h • strlen(str): liefert Länge des Strings str • strcpy(ziel, quelle): kopiert String quelle nach ziel • ziel wird als groß genug vorausgesetzt! • strcat(ziel, quelle): hängt String quelle an String ziel hinten an • ziel wird als groß genug vorausgesetzt! • strcmp(str1, str2): vergleicht die Strings str1 und str2 • ist das Ergebnis 0, sind die Strings gleich 13 Programm: Strings in C (Fortsetzung) # include < stdio .h > # include < string .h > int main () { char test_string [] = " test " ; char AuD_string [] = " AuD " ; char resultat_string [20]; // gross genug strcpy ( resultat_string , AuD_string ); printf ( " strcpy ! Ergebnis : ! % s \ n " , resultat_string ); int vergleich = strcmp ( resultat_string , AuD_string ); printf ( " strcmp ! Ergebnis : ! % d \ n " , vergleich ); strcat ( resultat_string , test_string ); printf ( " strcat ! Ergebnis : ! % s \ n " , resultat_string ); return 0; } 14 Strings in C - Probleme Probleme mit C Strings: • terminierende ’\0’ wird vergessen • Zuweisung nach Initialisierung schwierig • Größenvoraussetzungen werden nicht automatisch geprüft • Verwaltung von Strings dynamischer Länge eine Quelle der häufigsten Programmierfehler • aber gerade Strings dynamischer Länge werden gebraucht! 15 Strings in C++ Strings in C++: • dargestellt als std::string std :: string test_string ; • Zugriff wie bei C Strings über [] Operator, indiziert von 0 std :: string test_string = " test " ; test_string [1] = ’a ’; • automatische Verwaltung der Größe • nicht terminiert mit ’\0’, sondern speichert Größe direkt • Operationen direkt in std::string eingebaut 16 Programm: Strings in C++ # include < iostream > # include < string > using namespace std ; int main () { string test_string = " test " ; cout << " test_string : ! " << test_string << endl ; string AuD_string = " BvE " ; AuD_string [0] = ’A ’; AuD_string [1] = ’u ’; AuD_string [2] = ’D ’; cout << " AuD_string : ! " << AuD_string << endl ; cout << " Laenge : ! " << AuD_string . length () << endl ; ... 17 Programm: Strings in C++ (Fortsetzung) ... string resultat_string ; resultat_string = AuD_string ; cout << " Kopieren : ! " << resultat_string << endl ; bool vergleich = ( resultat_string == AuD_string ); cout << " Vergleich : ! " << vergleich << endl ; resultat_string = resultat_string + test_string ; // oder kuerzer : // resultat_string += test_string ; cout << " Anhaengen : ! " << resultat_string << endl ; return 0; } 18 Programm heute 1 Einführung 2 Mathematische Grundlagen Mengen Abbildungen Zahldarstellung Boolesche Logik 3 Elementare Datenstrukturen Zeichenfolgen Felder 19 Definition Feld Definition Feld Ein Feld A ist eine Folge von n Datenelementen (di )i=1,...,n , A = d1 , d2 , . . . , dn mit n ∈ N0 . Die Datenelemente di sind beliebige Datentypen (z.B. primitive). Beispiele: • A sind die natürlichen Zahlen von 1 bis 10, aufsteigend geordnet: A = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 • Ist n = 0, so ist das Feld leer. 20 Feld als sequentielle Liste Repräsentation von Feld A als sequentielle Liste (oder Array) • feste Anzahl n von Datenelementen • zusammenhängend gespeichert • in linearer Reihenfolge mit Index • Zugriff auf i-tes Element über Index i: A[i] Feld A: A[n] A[n-1] ... A[2] A[1] A[0] 21 Beispiel sequentielle Liste Feld A=0,8,15, Länge 3 • Beispiel in C: int A [3]; A [0] = 0; A [1] = 8; A [2] = 15; • Beispiel in C++: std :: vector < int > A (3); A [0] = 0; A [1] = 8; A [2] = 15; 22 Eigenschaften sequentielle Liste Feld A mit Länge n als sequentielle Liste (Array) • Vorteile: • direkter Zugriff auf Elemente in konstanter Zeit mittels A[i] • sequentielles Durchlaufen sehr einfach • Nachteile: • Verschwendung von Speicher falls Liste nicht voll belegt • Verlängern der sequentiellen Liste sehr aufwendig • Hinzufügen und Löschen von Elementen sehr aufwendig 23 C Beispiel: sequentielles Durchlaufen Code: int feldA [5]; int i ; // sequentielles Durchlaufen printf ( " feldA : ! " ); for ( i = 0; i < 5; ++ i ) { feldA [ i ] = i * i ; printf ( " % d ! " , feldA [ i ]); } printf ( " \ n " ); Ausgabe: feldA : 0 1 4 9 16 24 Verlängern der sequentiellen Liste Gegeben: Feld A, Länge n, als sequentielle Liste Gewünscht: Feld A erweitert auf Länge n+1 • neuen Speicher der Größe n+1 reservieren • alte Liste in neuen Speicher kopieren Feld A: neues Feld A: A[n+1] A[n] A[n-1] ... A[2] A[1] A[0] A[n] A[n-1] ... A[2] A[1] A[0] 25 C Beispiel: Verlängern der Liste Code: int neuesFeldA [6]; // altes Feld kopieren : for ( i = 0; i < 5; ++ i ) neuesFeldA [ i ] = feldA [ i ]; // neues Element : neuesFeldA [5] = 25; // neuesFeldA ausgeben : printf ( " neuesFeldA ! nach ! Verlaengern : ! " ); for ( i = 0; i < 6; ++ i ) printf ( " % d ! " , neuesFeldA [ i ]); printf ( " \ n " ); Ausgabe: neuesFeldA nach Verlaengern : 0 1 4 9 16 25 26 Löschen von Element aus Liste Gegeben: Feld A, Länge n, als sequentielle Liste Gewünscht: Element i aus Feld A löschen • Element i entfernen • Listenelemente nach i umkopieren 25 16 9 4 1 0 - 25 16 9 4 1 27 C Beispiel: Löschen von Element Code: // loesche erstes Element for ( i = 1; i < 6; ++ i ) neuesFeldA [i -1] = neuesFeldA [ i ]; // setze letztes Element auf -1 neuesFeldA [5] = -1; // neuesFeldA ausgeben : printf ( " neuesFeldA ! nach ! Loeschen : ! " ); for ( i = 0; i < 6; ++ i ) printf ( " % d ! " , neuesFeldA [ i ]); printf ( " \ n " ); Ausgabe: neuesFeldA nach Loeschen : 1 4 9 16 25 -1 28 Einfügen von Element in Liste Gegeben: Feld A, Länge n, als sequentielle Liste Gewünscht: neues Element in Feld A an Stelle i einfügen • Listenelemente nach i umkopieren • Element i einfügen - 25 16 9 4 1 25 16 9 8 4 1 29 C Beispiel: Einfügen von Element Code: // neues Element (8) an 3. Stelle einfuegen for ( i = 5; i > 2; --i ) neuesFeldA [ i ] = neuesFeldA [i -1]; neuesFeldA [2] = 8; // neuesFeldA ausgeben : printf ( " neuesFeldA ! nach ! Einfuegen : ! " ); for ( i = 0; i < 6; ++ i ) printf ( " % d ! " , neuesFeldA [ i ]); printf ( " \ n " ); Ausgabe: neuesFeldA nach Einfuegen : 1 4 8 9 16 25 30 C++ Beispiel: sequentielle Listen Code: vector < int > feldA (5); // sequentielles Durchlaufen for ( int i = 0; i < 5; ++ i ) { feldA [ i ] = i * i ; } // Verlaengern der Liste feldA . resize (6); feldA [5] = 25; // loesche erstes Element feldA . erase ( feldA . begin () ); feldA [5] = -1; // neues Element (8) an 3. Stelle einfuegen feldA . insert ( feldA . begin () + 2 , 8); 31 Ausblick: Anwendung von sequentiellen Listen in 2D und 3D Bildern! 32 Zusammenfassung 1 Einführung 2 Mathematische Grundlagen Mengen Abbildungen Zahldarstellung Boolesche Logik 3 Elementare Datenstrukturen Zeichenfolgen Felder 33