Computer Science Prof. Dr. Katrin Brabender Labor für Angewandte Informatik und Datenbanken Version: 15.08.2006 WS 2006/2007 Computer Science Seite 1 Inhalte der Vorlesung Algorithmen und Datenstrukturen • Lineare und Nichtlineare Datenstrukturen • Formale Beschreibung von Algorithmen • Suchalgorithmen • Wörterbücher • Hash-Verfahren • Binäre Suchbäume, Balancierte Bäume etc. • Sortieralgorithmen • Geometrische Algorithmen • Graphen Algorithmen Grundlagen der Automatentheorie WS 2006/2007 Computer Science Seite 2 1 Literatur Bernd Breutmann: Data and Algorithms, Fachbuchverlag Leipzig,2th edition, 2001. Ralf Hartmut Güting, Stefan Dieker: Datenstrukturen und Algorithmen, 2. Auflage, 2003. H.-P. Gumm, M. Sommer: Einführung in die Informatik, 5. Auflage, 2002. Volker Heun: Grundlegende Algorithmen. Vieweg, Braunschweig Wiesbaden, 2000. Gunter Saake, Kai-Uwe Sattler: Algorithmen und Datenstrukturen. Eine Einführung mit Java. dpunkt.Verlag, 3.Auflage 2006. Thomas Ottmann, Peter Widmayer: Algorithmen und Datenstrukturen. Spektrum Akademischer Verlag, Heidelberg Berlin, 3. Auflage, 1996. Uwe Schöning: Theoretische Informatik-kurzgefasst, Spektrum Akademischer Verlag, Stuttgart Leipzig, 4. Aufl., 2001. Gottfried Vossen, Kurt-Ulrich Witt: Grundlagen der Theoretischen Informatik mit Anwendungen, Vieweg, 2. Aufl. ,2002. Niklaus Wirth: Algorithmen und Datenstrukturen, B.G. Teubner, Stuttgart Leipzig, 1999. WS 2006/2007 Computer Science Seite 3 Teil 1: Algorithmen und Datenstrukturen WS 2006/2007 Computer Science Seite 4 2 Datenstrukturen • Algorithmen und Datenstrukturen sind ein zentrales Thema in der Informatik. • Algorithmen operieren nur dann effektiv mit Daten, wenn diese für einen Algorithmus geeignet strukturiert sind. • Die Wahl der richtigen Datenstruktur entscheidet über effiziente Laufzeiten der Algorithmen. WS 2006/2007 Computer Science Seite 5 Datentypen Unter Datentyp versteht man eine Menge von Werten und von Operationen. Eine Operation ist dabei eine Verknüpfung, die einer festen Anzahl von Eingabedaten ein Ergebnis zuordnet. Zu einem bestimmten Datentyp gehört immer ein charakteristischer Satz von Operationen. Beispiele Jede Programmiersprache hat einen Datentyp, der die Menge der ganzen Zahlen integer, reellen Zahlen real, Zeichen char und Wahrheitswerte boolean repräsentiert. WS 2006/2007 Computer Science Seite 6 3 Datentyp: Boolean Werte: true, false Operationen: or, and, xor : Boolean x Boolean -> Boolean not : Boolean -> Boolean true, false : -> Boolean Gleichungen: Für alle x, y aus Boolean gilt x or y = y or x x and y = y and x true or x = true true and x = x false or x = x false and x = false x xor y = (x and not y) or (not x and y) In Java werden and , or , not , xor durch && , || , ! , ^ dargestellt. WS 2006/2007 Computer Science Seite 7 Datentyp: Integer Werte: alle ganzen Zahlen Operationen: +, -, *, div, mod : Integer x Integer -> Integer Gleichungen: Für alle x, y aus Integer gilt 0+x=x 0*x=0 (x div y)* y + x mod y = x x – y = x + (-y) (-x div y) = -(x div y) = x div –y (-x mod y) = - (x mod y) x mod –y = x mod y In Java, C und C++ werden div und mod durch / und % dargestellt. WS 2006/2007 Computer Science Seite 8 4 Datentyp: Real Werte: alle reellen Zahlen Operationen: +, -, *, / : Real x Real -> Real sqrt, ln, sin, exp : Real -> Real Gleichungen: Für alle x, y aus Real gilt 0+x=x 0*x=0 sprt(x) * sqrt(x) = x …. WS 2006/2007 Computer Science Seite 9 Datentyp: Char Werte: alle ASCII-Zeichen Operationen: chr, ord Gleichungen: ord(chr(n)) = n, chr(ord(c))) = c Die grundlegenden Datentypen, d.h. integer, real, boolean und char bezeichnet man als Elementare- oder Primitive Datentypen. WS 2006/2007 Computer Science Seite 10 5 Pointer Ein Pointer (Zeiger) ist ein Verweis auf eine Objektrepräsentation im Hauptspeicher, d.h. eine Adresse im Hauptspeicher. Falls ein Zeiger auf kein Objekt zeigt, hat er den Wert NULL (NIL). Man nennt ihn Nullpointer. In Programmiersprachen wie C und Pascal werden Zeiger explizit gekennzeichnet. Sie müssen erzeugt werden. Wird der Speicherplatz nicht mehr benötigt, d.h. zeigt kein Zeiger mehr auf ihn, so muss der belegte Speicherplatz bei diesen Programmiersprachen vom Programmierer wieder freigegeben werden (z.B. mit dispose(p)) WS 2006/2007 Computer Science Seite 11 Zeiger in Java In Java werden Objekte (d.h. Instanzen von Klassen) durch Zeiger realisiert. Man spricht von Referenzdatentypen im Gegensatz zu den primitiven Datentypen. Durch die sog. Garbage Collection wird der nicht mehr benötigte Speicher automatisch freigegeben. WS 2006/2007 Computer Science Seite 12 6 Beispiel: Referenzdatentype u. primitiver Datentyp public class Int { private int wert; //Konstruktor Int (int wert) { this.wert = wert; } } Mit den Variablen int i = 2; Int j = new Int(2); Die Variable i repräsentiert einen Wert vom Typ int. Die Variable j referenziert ein Objekt vom Typ Int. WS 2006/2007 Computer Science Seite 13 Definition Eine Datenstruktur ist eine Sammlung von Knoten derselben Klasse oder desselben Typs, deren Organisation und Zugriff klar definiert sind. Ein Knoten kann ein Objekt oder auch primitive Datentypen, wie eine Zahl sein. Wir werden uns zunächst mit den linearen Datenstrukturen beschäftigen. WS 2006/2007 Computer Science Seite 14 7 Lineare Datenstrukturen Linear bedeutet, dass es zu jedem Element (Knoten) höchstens einen Vorgänger und höchstens einen Nachfolger gibt. Die einfachste lineare Datenstruktur ist das Array. WS 2006/2007 Computer Science Seite 15 Arrays • Ein Array ist die einfachste lineare Datenstruktur. • In einem Array wird eine endliche Menge von Elementen des selben Typs gespeichert. • Die einzelnen Elemente heißen Komponenten und können über Indizes angesprochen werden. • Man bezeichnet die k-te Komponente eines Arrays a mit a[k] oder ak • In Java und C werden die Komponenten eines Arrays ab 0 nummeriert. Mit length erhält man die Anzahl der Komponenten des Arrays WS 2006/2007 Computer Science Seite 16 8 Bemerkung zu Arrays • Die Größe eines Arrays wird zum Zeitpunkt der Erstellung des Arrays festgelegt, danach kann sie nicht mehr verändert werden. Ein Array ist eine lineare, statische Datenstruktur. Nachteile eines Arrays • Belegung von viel Speicherplatz bei großen Datenmengen • u. U. aufwendiges Einfügen von Elementen • Array kann „voll“ sein WS 2006/2007 Computer Science Seite 17 Listen Eine Liste ist eine Folge von Elementen, in der an beliebiger Stelle neue Elemente eingefügt oder vorhandene Elemente entfernt werden können. Wie kann man Listen implementieren? Einfachste Methode: Listenelemente werden in einem Array gespeichert. Nachteil: • Beim Anlegen muss festgelegt werden, wie viele Datensätze maximal gespeichert werden • Speicherplatz wird u.U. verschenkt oder Array ist voll • Einfügen eines Elementes bedeutet Verschiebung anderer Elemente => Implementierung durch lineare dynamische Datenstruktur. WS 2006/2007 Computer Science Seite 18 9 Lineare dynamische Datenstrukturen Dynamische Datenstrukturen passen ihre Größe der Anzahl der Daten an, die sie aufnehmen. WS 2006/2007 Computer Science Seite 19 Einfach verkettete Liste • Eine einfach verkettete Liste ist eine lineare dynamische Datenstruktur. • Sie besteht aus einer Folge von Zellen / Knoten. • Jeder Knoten enthält neben dem „Daten-Element“ einen Zeiger (Referenz) Next auf den folgenden Knoten der Liste. • Die Liste hat einen Anfangszeiger, der auf den ersten Knoten zeigt. • Der Zeiger des letzten Knotens zeigt auf NULL. Anfang Inhalt 1 Inhalt 2 Inhalt Inhalt 3 n NULL WS 2006/2007 Computer Science Seite 20 10 Entfernen und Einfügen eines Elementes erfolgen durch einfaches Kopieren der Referenzen. Durch Garbage Collection wird die entfernte Zelle aus dem Speicher entfernt. Zunächst betrachten wir eine Implementierung einer verketteten Liste. WS 2006/2007 Computer Science Seite 21 Knoten einer Liste in Java public class Knoten { private Object inhalt; private Knoten next; next ist wieder vom Datentyp Knoten // Konstruktor: Knoten (Object inhalt) { this.inhalt = inhalt; } Knoten (Object inhalt, Knoten next) { this.inhalt = inhalt; this.next = next; } //get- und set-Methoden public Knoten getNext(){ return next; } WS 2006/2007 Computer Science Seite 22 11 public Object getInhalt(){ return inhalt; } public void setNext(Knoten next){ this.next = next; } public void setInhalt(Object inhalt){ this.inhalt = inhalt; } } WS 2006/2007 Computer Science Seite 23 Implementierung einer verketteten Liste public class Liste { private Knoten anfang; private Knoten cursor; //Ermittlung der Laenge der Liste int laenge () { Knoten cur = anfang; int l = 0; while (cur != null) { l++; cur = cur.getNext(); } return l; } WS 2006/2007 Computer Science Seite 24 12 //Hilfsmethoden zum korrekten Setzen der Referenz Cursor boolean istGueltigePosition (int p) { return (p >= 0) && (p < laenge() ); } void setzeCursor (int p){ cursor = null; if (istGueltigePosition (p) ) { Knoten cur = anfang; for (int i = 0; i < p;i++) cur = cur.getNext(); cursor = cur; } } WS 2006/2007 Computer Science Seite 25 //Methode, die Inhalt des Objektes eines Knotens liefert Object inhalt (int p){ setzeCursor (p); return cursor.getInhalt(); } //Methode zum Löschen des p-ten Knotens in Liste void loesche (int p) { if (istGueltigePosition(p)){ if (p == 0) // Loesche 1. Zelle anfang = anfang.getNext(); else { setzeCursor(p-1); cursor.setNext(cursor.getNext().getNext()); } } } WS 2006/2007 Computer Science Seite 26 13 //Methoden zum Einfügen eines Objektes in Liste void einsetzenNach (int p, Object e){ setzeCursor (p); if (cursor != null) { Knoten z = new Knoten (e, cursor.getNext()); cursor.setNext(z); } } void einsetzenVor (int p, Object e){ if (p > 0) einsetzenNach (p-1,e); else {// Einsetzen am Anfang Knoten z = new Knoten (e, anfang); anfang = z; } } void einsetzenAnfang (Object e){ Knoten z = new Knoten (e,anfang); anfang = z; } } WS 2006/2007 Computer Science Seite 27 Spezielle Listen - Stapel (stack) und Schlange (queue) Operationen können nur an einem Ende ausgeführt werden. Stapel Einfügen und Entfernen nur am Kopf (Anfang) der Liste, d.h. LIFO (last in, first out); Abfertigung in umgekehrter Reihenfolge der Ankunft. Schlange Einfügen am Ende, Entfernen am Kopf, d.h. FIFO (First in, first out) Abfertigung in Reihenfolge des Eintreffens. WS 2006/2007 Computer Science Seite 28 14 Die Datenstrukturen Keller (Stack) Ein Keller oder Stack hat die Eigenschaften • Knoten sind in einer Folge angeordnet • Nur der Anfang des Stacks ist zugänglich • Knoten werden am Anfang hinzugefügt • Knoten werden vom Anfang entfernt. • Der Keller kann nicht durchsucht werden, nur der Knoten am Anfang ist sichtbar. Aufgrund dieser Eigenschaften bezeichnet man einen Keller aus als LIFO-Liste (Abkürzung für Last In First Out). WS 2006/2007 Computer Science Seite 29 Eine Anwendung von Stacks ist die Verwaltung rekursiver Methoden. Die wichtigsten Methoden für einen Stack sind die Methoden push() und pop() zum Einfügen und Entfernen aus dem Keller. Um die Funktionalität von diese beiden Methoden zu verstehen, implementieren wir eine Klasse Keller mit den beiden Methoden. Keller soll durch ein Array dargestellt werden. WS 2006/2007 Computer Science Seite 30 15 Implementierung eines Kellers class Keller{ private int laenge; private Object[] kellerarray; private int top; //index der nächsten freien Zelle //Konstruktor Keller (int laenge){ this.laenge = laenge; kellerarray = new Object[laenge]; top = 0; //Setzen von top auf die erste Position } WS 2006/2007 Computer Science Seite 31 Die Methoden push() und pop() void push (Object x){ if (top >= laenge) System.out.println("Keller voll"); else { kellerarray[top] = x; top++; } } Object pop(){ if (top > 0){ top--; return kellerarray[top]; } else return null; } } WS 2006/2007 Computer Science Seite 32 16 Die Datenstrukturen Schlange (Queue) Eine Schlange oder Queue hat die Eigenschaften • Knoten sind in einer Folge angeordnet • Es gibt zwei Enden, den Anfang (head) und das Ende (tail) • Knoten werden am Ende hinzugefügt • Knoten werden vom Anfang entfernt. • Die Schlange kann von Anfang bis zum Ende durchsucht werden Aufgrund dieser Eigenschaften bezeichnet man eine Schlange auch als FIFO-Liste (Abkürzung für First In First Out). WS 2006/2007 Computer Science Seite 33 Eine Schlange kann mittels einer verketteten Liste oder mit Hilfe eines Arrays implementiert werden. Wir werden hier –wie schon beim Stack- das Array nehmen. Die zwei wichtigsten Methoden sind enqueue(element) zum Einfügen in eine Queue und dequeue() zum Entfernen aus einer Queue. WS 2006/2007 Computer Science Seite 34 17 Implementierung einer Schlange class Schlange{ private private private private int laenge; Object[] schlangearray; int head, tail; boolean istleer; //Konstruktor Schlange (int laenge){ this.laenge = laenge; head = tail = 0; istleer = true; schlangearray = new Object[laenge]; } WS 2006/2007 Computer Science Seite 35 Die Methode enqueue() void enqueue (Object x){ if ((tail == head) && (istleer == false)){ System.out.println("Queue ist voll"); } else{ schlangearray[tail] = x; tail++; istleer = false; if (tail == laenge) tail = 0; } } WS 2006/2007 Computer Science Seite 36 18 Die Methode dequeue() Object dequeue(){ if ((tail == head) &&(istleer == true)){ System.out.println("Queue ist leer"); return null; } else { Object inhalt = schlangearray[head]; head++; if (head == laenge) head = 0; if (head == tail) istleer = true; return inhalt; } } } WS 2006/2007 Computer Science Seite 37 Zusammenfassung Behandelt wurden lineare Datenstrukturen • Eine Datenstruktur ist eine Sammlung von Knoten derselben Klasse oder desselben Typs, deren Organisation und Zugriff klar definiert sind. • Linear bedeutet, dass jedes Element höchstens einen Vorgänger und höchstens einen Nachfolger hat. • Dynamisch bedeutet, dass sich die Größe der Datenstruktur zur Laufzeit verändern kann • Arrays sind lineare statische Datenstrukturen, dessen Elemente vom selben Typ sind • Einfach verkettete Listen sind lineare dynamische Datenstrukturen WS 2006/2007 Computer Science Seite 38 19 Nichtlineare Datenstrukturen Bäume • Bäume gehören zu den fundamentalen Datenstrukturen der Informatik. • Sie können als zweidimensionale Verallgemeinerung von Listen gesehen werden. • In Bäumen kann man nicht nur Daten, sondern auch relevante Beziehungen der Daten untereinander speichern • Bäume sind Datenstrukturen, die besonders für das schnelle Finden von Daten geeignet sind WS 2006/2007 Computer Science Seite 39 Grundbegriffe innerhalb eines Baumes Ein Baum besteht aus einer Menge von Knoten, die untereinander durch Kanten verbunden sind. Führt von Knoten A zu Knoten B eine Kante, so schreibt man A -> B und sagt A ist Elternknoten (parent, Vater) von B oder B ist Kind von A (Sohn). Ein Pfad von A nach B ist eine Folge von Knoten und Kanten, die von A nach B führen. A ->X1 ->X2->…->B. (A ist dabei Elternknoten von X1….) Die Länge des Pfades definiert man als Anzahl der Kanten auf dem Pfad. Knoten ohne Kind nennt man Blatt. Alle anderen Knoten heißen innere Knoten. WS 2006/2007 Computer Science Seite 40 20 Ein Baum muss folgende Axiome erfüllen • Es gibt genau einen Knoten ohne Elternknoten. Diesen nennt man Wurzel. • Jeder andere Knoten hat genau einen Eltern-Knoten. Definition Baum (rekursiv) Ein Baum ist leer oder er besteht aus einer Wurzel W und einer leeren oder nicht leeren Liste B1,B2,…,Bn von Bäumen. Von W zur Wurzel W i von Bi führt jeweils eine Kante. WS 2006/2007 Computer Science Seite 41 Tiefe eines Knotens k Länge des Pfades von der Wurzel zu diesem Knoten k. Höhe eines Baumes 0 falls leerer Baum, sonst Maximum der Tiefe seiner Knoten. Beispiele von Bäumen: • Stammbäume • Dateibäume (Knoten sind Dateien oder Verzeichnisse, Kanten führen von einem Verzeichnis zum Unterverzeichnis) • Organisationsstruktur eine Firma WS 2006/2007 Computer Science Seite 42 21 Binärbäume Bäume mit der Eigenschaft, dass jeder Knoten nur 2 Kinder hat. Definition Binärbaum Ein Binärbaum ist • leer oder • besteht aus einem Knoten – Wurzel genannt – und zwei Binärbäumen, dem rechten und linken Teilbaum. WS 2006/2007 Computer Science Seite 43 Traversierung eines Binärbaums Eine Traversierung eines Baumes zählt alle Elemente, die in einem Baum sind, in einer bestimmten Reihenfolge auf. Die wichtigsten Durchläufe sind Preorder Postorder Inorder Levelorder WS 2006/2007 Computer Science Seite 44 22 Ein binärer Baum B heißt vollständiger Baum, wenn er folgende Form hat: Also: für jedes k mit k < Höhe (B) gilt: • die k-te Schicht ist voll besetzt (ein Baum beginnt auf Schicht 0, d.h. B besitzt 2k Knoten der Tiefe k) • die letzte Schicht ist, von links nach rechts gelesen, bis zu einem Punkt P besetzt WS 2006/2007 Computer Science Seite 45 Heaps Ein Array a heißt Heap, wenn es ein vollständiger Baum ist, für den gilt a[i] >= a[2i+1] und a[i] >= a[2(i+1)] Ein Heap ist also ein Binärbaum mit den beiden Eigenschaften 1. Form 2. Ordnung: Entlang jedes Pfades von einem Knoten zur Wurzel sind die Knoteninhalte aufsteigend sortiert WS 2006/2007 Computer Science Seite 46 23 Algorithmen Algorithmen sind Verfahren zur schrittweise Lösung von Problemen. Sie sind Grundlage für jedes Computer-Programm. Beispiel: Bestimmung des größten gemeinsamen Teilers Gegeben seine zwei positive ganze Zahlen m und n. Bestimme den größten gemeinsamen Teiler von m und n. 1. (Vertausche m und n) m ↔n 2. (Übertrage an n den Wert n modulo m) n ← n % m 3. (Ist n größer 0?) Falls n >0 gehe zurück zu 1. Falls n = 0 ist Algorithmus beendet. Ergebnis ist m. WS 2006/2007 Computer Science Seite 47 Pseudocode Algorithmen sind Programmiersprachen unabhängig. Eine gebräuchliche Art Algorithmen darzustellen ist die Benutzung von Pseudocode. Pseudocode ist eine künstliche Sprache, die den höheren Programmiersprachen wie Java, C oder Pascal ähnelt. Anhand des Pseudocodes kann der Algorithmus direkt in die entsprechende Programmiersprache übertragen werden. WS 2006/2007 Computer Science Seite 48 24 Notation im Pseudocode 1. In der ersten Zeile erscheint der Name des Algorithmus, gefolgt von den geforderten Parametern 2. Einrücken zeigt Blockstruktur. Zusätzlich wird eine Blockstruktur durch {} gekennzeichnet. 3. Als Kontroll-Strukturen benutzen wir die Schlüsselwörter while, for, if…else 4. Kommentare werden durch // angezeigt. 5. Wir benutzen Semikolons am Ende einer Anweisung WS 2006/2007 Computer Science Seite 49 Basiselemente eines Algorithmus 1. Operationen wie n % m, a + b 2. Anweisungen und Zuweisungen unterschieden werden input, output Anweisungen. Sie sind zuständig für den Datenfluss ins und aus dem System Zuweisungen werden durch ← ausgedrückt (m ← n, m erhält Wert von n); m ↔ n bedeutet t ← m; m ← n; n ← t 3. Kontrollstrukturen Es gibt drei Arten von Kontrollstrukturen Sequenz-Struktur, Verzweigung, Schleife WS 2006/2007 Computer Science Seite 50 25 • Sequenz-Struktur, d.h. Anweisungen werden hintereinander ausgeführt In Pseudocode anweisung 1; M anweisung n; • Verzweigung (if-Anweisung) In Pseudocode if (bedingung){ anweisung a1;…;anweisung am; } else { anweisung b1;…;anweisung bn; } WS 2006/2007 Computer Science Seite 51 • Schleife oder Wiederholungs-Struktur (while oder for) In Pseudocode while (bedingung) {anweisung 1;…;anweisung n;} oder M for (i = 1 to m) {anweisung 1;…;anweisung n;} WS 2006/2007 Computer Science Seite 52 26 Definition Algorithmus Ein Algorithmus ist eine endliche Folge von Anweisungen. Ein Algorithmus benötigt eine Menge von Werten (diese kann auch leer sein) als Input und generiert eine Menge von Werten als output (diese darf nicht leer sein). WS 2006/2007 Computer Science Seite 53 Ein Algorithmus hat vier wichtige Eigenschaften 1. (endlich) Nach einer Anzahl von endlichen Schritten ist der Algorithmus beendet 2. (exakt) Jeder Schritt des Algorithmus ist präzise definiert 3. (elementar) Alle Operationen sind auf elementare Operationen zurückzuführen 4. (input) Ein Algorithmus hat null oder mehr input-Daten 5. (output) Ein Algorithmus hat ein oder mehrere output-Daten WS 2006/2007 Computer Science Seite 54 27 Kriterien für die Qualität von Algorithmen 1. Korrektheit 2. Einfach zu verstehen (besonders für Wartung wichtig) 3. Einfach zu implementieren 4. Geringe Laufzeit und geringe Plattenplatz (Komplexität des Algorithmus) Bemerkung: Die einzelnen Kriterien können sich auch widersprechen, z.B. können effiziente Algorithmen sehr unübersichtlich sein. WS 2006/2007 Computer Science Seite 55 Die Laufzeit T von Algorithmen Unter der Laufzeit T(n) eines Algorithmus auf einer bestimmten InputMenge der Größe n versteht man die Anzahl der auszuführenden Schritte. Für jede Anweisung wählt man eine konstante Zeit t0. Diese Zeit t0 ist vom Algorithmus unabhängig und nur von dem Computer abhängig. Gewöhnlich setzt man t0 = 1. Es wird unterschieden worst-case: bestimmt die obere Grenze der Laufzeit für jeden Input best-case: bestimmt die untere Grenze der Laufzeit für jeden Input average-case: bestimmt die Laufzeit für einen typischen Input, d.h. den Durchschnitt WS 2006/2007 Computer Science Seite 56 28 T(n) Notation ln n, log n, log 10 n logarithmische Zeitkomplexität n, n2, n3 polynomiale Zeitkomplexität 2n, en exponentielle Zeikomplexität Ein Algorithmus heißt effizient, wenn er mindestens polynomiale Zeitkomplexität hat. WS 2006/2007 Computer Science Seite 57 Durch Weglassen von multiplikativen und additiven Konstanten wird nur noch das Wachstum einer Laufzeitfunktion T(n) betrachtet: Definition (O -Notation) Sei g (n) eine gegebene Funktion. Mit O(g(n)) bezeichnet man die Menge aller Funktionen, die kleiner gleich einer Konstanten multipliziert mit g ab einem bestimmten endlichen Wert n 0 ist. D.h. f wächst höchstens so schnell wie g. Anders ausgedrückt: { O ( g (n)) = f (n) | ∃c ∈ R + , n0 ∈ N : 0 ≤ f (n) ≤ c ∗ g (n) ∀ n ≥ n0 Statt T ( n) ∈ O ( g ( n)) schreibt man auch } T (n) = O( g(n)) WS 2006/2007 Computer Science Seite 58 29 Zusammenfassung • Ein Algorithmus ist eine endliche Folge von Anweisungen, die durch 3 Kontrollstrukturen kontrolliert werden. • Es werden Inputdaten in Output-Informationen transformiert • Die Komplexität wird gemessen durch das Zählen der Anzahl der Anweisungen, die ausgeführt werden; Grundlage ist die RAM, ein idealisiertes mathematisches Modell eines Computers • Ein Algorithmus mit mindestens polynomialer Zeitkomplexität ist effizient. WS 2006/2007 Computer Science Seite 59 Suchalgorithmen Formulierung des Suchproblems In einem Behälter A befinden sich eine Reihe von Elementen. Prüfe, ob ein Element e ∈ A existiert, das eine bestimmte Eigenschaft P(e) erfüllt. Behälter sind z.B. Arrays, Listen, Bäume etc. Wir betrachten zunächst Algorithmen auf Arrays. Die Werte im Array seien Zahlen. WS 2006/2007 Computer Science Seite 60 30 Das Suchproblem lautet also für Arrays: Sei a[] ein Array der Länge n. Prüfe, ob Element e im Array vorhanden ist, d.h. existiert ein i für das gilt: a[i] == e für i = 0,…,n-1. WS 2006/2007 Computer Science Seite 61 Lineare Suche Die lineare Suche ist der einfachste Suchalgorithmus. Das Array wird der Reihe nach durchsucht. Die Länge des Arrays sei n. Der Algorithmus lautet LinSuche (a[], e) { for (i = 0; i < n; i++){ if (a[i] = = e) return i; } return –1; //ungültiger Index } Es gilt Tbest = O (1) Taverage = Tworst = O(n) WS 2006/2007 Computer Science Seite 62 31 Binäre Suche Voraussetzung: Suche auf geordnetem bzw. sortiertem Array a. Definition: a heißt geordnet (oder sortiert) wenn gilt: ∀ i : 0 ≤ i ≤ n − 2 ist a[i ] ≤ a[i + 1] WS 2006/2007 Computer Science Seite 63 Idee der Binären Suche Sei 0 = min, n - 1 = max Wähle Index m mit min ≤ m ≤ max (meist m = (min+max)/2) - Falls e = a[m] => Fertig, Element gefunden - Falls e < a[m] => Suche weiter im Bereich [min,m-1], d.h. max = m-1 - Falls e > a[m] => Suche weiter im Bereich [m+1;max], d.h. min = m+1 Abbruch, falls min > max, dann ist e kein Element des Arrays. WS 2006/2007 Computer Science Seite 64 32 algorithm RekBinSuche (a[], e, min, max) { if (min > max) return –1; m am ← (min + ← a[m]; max) / 2; if (e = = am) return m; if (e < am) return RekBinSuche (a[],e,min,m-1); if (e > am) return RekBinSuche (a[],e,m+1,max); } return –1; WS 2006/2007 Computer Science Seite 65 Die binäre Suche ist für große Datenmengen weit effizienter als die lineare Suche. Es gilt: Taverage = Tworst = O (log 2 n ) Im besten Fall ist nur ein Aufruf des Suchalgorithmus nötig. Aber: Die Elemente müssen sortiert vorliegen. Daher oft bei großen nicht sortierten Datenmengen sinnvoll: Kopiere alle Elemente in Array, sortiere Array, wende binäre Suche an. WS 2006/2007 Computer Science Seite 66 33 Wörterbücher (Dictionaries) Als Wörterbuch wird eine Menge von Elementen eines gegebenen Grundtyps bezeichnet, auf der man die Operationen Suchen, Einfügen und Entfernen von Elementen ausführen kann. Man nimmt meistens an, dass alle Elemente über einen in der Regel ganzzahligen Schlüssel (key) eindeutig identifizierbar seien. Such-, Einfüge- und Entferne-Operationen werden nur auf dem jeweiligen Schlüssel durchgeführt. Implementierungsmöglichkeiten: Listen, Hashtabellen, Bäume. WS 2006/2007 Computer Science Seite 67 Streu-Verfahren (Hash-Verfahren) Die Grundidee von Hashverfahren besteht darin, aus dem Wert eines zu speichernden Mengenelementes seine Adresse im Speicher zu berechnen. Den Speicher zur Aufnahme der Mengenelemente fasst man als eine Menge von Körben B0,…BB-1 auf. Der Wertebereich D, aus dem die Mengenelemente stammen können, kann beliebig groß sein (meist |D| >> B). WS 2006/2007 Computer Science Seite 68 34 Eine Hashfunktion h : D → {0,..., B − 1} , B Anzahl der Körbe, ordnet jedem Schlüssel x ∈ D einen Index h(x) mit 0 ≤ h( x) ≤ B −1 zu. h(x) = b mit b Korb. Eine Hashfunktion sollte folgende Eigenschaften haben: • surjektiv, d.h. sie sollte alle Körbe erfassen • sie sollte die zu speichernden Schlüssel möglichst gleichmäßig über alle Körbe verteilen • effizient zu berechnen sein WS 2006/2007 Computer Science Seite 69 Werden mehrere Werte auf den selben Korb abgebildet, so nennt man das Kollision. Hashverfahren unterscheiden sich in der Art der Kollisionsbehandlung. Beim offenem Hashing nimmt man an, dass ein Korb beliebig viele Schlüssel aufnehmen kann. Beim geschlossenen Hashing kann ein Korb nur eine kleine konstante Anzahl b von Schlüsseln aufnehmen; falls mehr als b Schlüssel auf einen Korb fallen, entsteht ein Überlauf. WS 2006/2007 Computer Science Seite 70 35 Offenes Hashing (Open Hashing) Ein Korb kann beliebig viele Schlüssel aufnehmen. Zur Implementierung Die Hashtabelle ist ein array der Größe B, das aus verketteten linearen Listen besteht. D.h. aus Zeigern, die auf Elemente des Typs Knoten zeigen. NULL WS 2006/2007 Computer Science Seite 71 Beim Einfügen, Entfernen und Suchen eines Schlüssels k wird beim Open Hashing jeweils die Liste Hashtabelle[i] mit i = h(k) durchlaufen. Einfügen Das neue Element wird als erstes Element in diese Liste eingefügt. Suchen Beim Suchen eines Elementes die entsprechende Liste sukzessiv durchsucht. WS 2006/2007 Computer Science Seite 72 36 Die einfachste Hash-Funktion ist die Modulo-Funktion h(x) = x mod B mit Körben 0,…,B-1 Zeitberechnung: O (1) Durchschnitt und worst-case bei Einfügen, da stets erstes Element. Nachteil bei Open-Hashing: Hoher Speicherplatzbedarf. Die schlechteste Hashfunktion lautet h(x) = Konstante für alle x aus D. WS 2006/2007 Computer Science Seite 73 Geschlossenes Hashing (Closed Hashing) • Jeder Korb kann nur eine kleine konstante Anzahl b (meist b = 1) von Schlüsseln aufnehmen; • Wir nehmen b = 1 an: • Falls mehr als 1 Schlüssel auf einen Korb fällt, entsteht ein Überlauf: Damit ist für das geschlossene Hashing die Kollisionsbehandlung von entscheidender Bedeutung: WS 2006/2007 Computer Science Seite 74 37 Kollisionsbehandlung durch rehashing Neben der Hashfunktion h = h0 existieren weitere Hashfunktionen h1,…hB-1, die für einen gegebenen Schlüssel x die Körbe h(x), h1(x),h2(x)… untersuchen. Einfügen Sobald ein freier oder als gelöscht markierter Korb gefunden ist, wird x dort eingetragen. Suchen Bei der Suche nach x wird h(x), h1(x),h2(x)… betrachtet bis entweder x gefunden oder erster freier Korb in dieser Folge gefunden. Dann ist x nicht vorhanden. WS 2006/2007 Computer Science Seite 75 Wichtig: Es muss unterschieden werden, ob Zelle frei oder gelöscht ist. Eine einfache Rehash-Funktion ist hi ( x ) = ( h ( x ) + i ) mod B , 1 ≤ i ≤ B − 1 Man nennt diese Rehash-Funktion Lineares Sondieren. Sie hat den Nachteil, dass Elemente oft nicht einmal sondern mehrfach rehashed werden müssen. Besser: quadratisches Sondieren hi ( x ) = ( h ( x ) + i 2 ) mod B , 1 ≤ i ≤ B − 1 WS 2006/2007 Computer Science Seite 76 38 Binäre Suchbäume Definition Ein binärer Suchbaum hat die Eigenschaften: - leer oder - besteht aus einem Knoten – Wurzel genannt- und zwei binären Suchbäumen, dem linken und rechten Teilbaum. Der Inhalt des Wurzelknotens ist größer oder gleich allen Elementen im linken Suchbaum und kleiner als alle Elemente im rechten Suchbaum. Binäre Bäume sind Wörterbuch-Datenstrukturen. WS 2006/2007 Computer Science Seite 77 Implementierung von Binären Suchbäumen in Java. Die Inhalte der Knoten sind hier Integer Werte. class Knoten { private int inhalt; private Knoten links; private Knoten rechts; // Konstruktoren: public Knoten (int inhalt) { this.inhalt = inhalt; } public Knoten(int inhalt, Knoten links,Knoten rechts){ this.inhalt = inhalt; this.links = links; this.rechts = rechts; } public int getInhalt(){ return inhalt; } WS 2006/2007 Computer Science Seite 78 39 public Knoten getLinks(){ return links; } public Knoten getRechts(){ return rechts; } public void setInhalt(int inhalt){ this.inhalt = inhalt; } public void setLinks(Knoten links){ this.links = links; } public void setRechts(Knoten rechts){ this.rechts = rechts; } } WS 2006/2007 Computer Science Seite 79 Nachdem die Klasse Knoten definiert wurde, kann nun die Klasse Baum definiert werden. Ein Baum besteht aus einer Wurzel vom Datentyp Knoten, sowie aus Methoden zum Einfügen, Löschen und Suchen eines Knoten. public class Baum { private Knoten wurzel; public Knoten getWurzel(){ return wurzel; } //… Methoden } WS 2006/2007 Computer Science Seite 80 40 Wir nehmen für die Operationen Suchen, Einfügen und Entfernen an, dass keine Duplikate im Baum vorhanden sind. Suchen eines Elementes x innerhalb eines Binären Suchbäumen B Vergleiche x mit Wurzel W: Es treten drei Fälle auf x = W => Element gefunden x < W => Suche rekursiv im linken Teilbaum von B x > W => Suche rekursiv im rechten Teilbaum von B WS 2006/2007 Computer Science Seite 81 Implementierung der Suche eines Elementes innerhalb eines Binären Suchbaumes in Java boolean suche (int e){ if (wurzel == null) return false; else return rekSuche (e,wurzel); } boolean rekSuche (int e, Knoten k){ if ( k == null) return false; if ( e == k.getInhalt()) return true; if (e < k.getInhalt()) return rekSuche (e, k.getLinks()); else return rekSuche (e, k.getRechts()); } WS 2006/2007 Computer Science Seite 82 41 Einfügen eines Elementes x in einen Binären Suchbäumen B Falls Baum noch leer => Lege x als neue Wurzel an Falls Baum nicht leer => Suche richtige Position im Baum und lege neuen Knoten an dieser Stelle an. WS 2006/2007 Computer Science Seite 83 Implementierung des Einfügens eines Elementes in einen Binären Suchbaum void einfuegen (int e){ if (wurzel == null) wurzel = new Knoten (e); else rekEinfuegen (e,wurzel); } void rekEinfuegen (int e, Knoten k){ if (e < k.getInhalt()) if (k.getLinks() == null) k.setLinks(new Knoten (e)); else rekEinfuegen (e,k.getLinks()); else if (k.getRechts() == null) k.setRechts(new Knoten (e)); else rekEinfuegen (e,k.getRechts()); } WS 2006/2007 Computer Science Seite 84 42 Löschen eines Elementes x aus einem Binären Suchbaum B Das Löschen ist schwieriger als das Einfügen und Suchen. Das zu löschende Element x kann irgendwo in der Mitte sein, eventuell an der Wurzel. Dann muss umgehängt werden. WS 2006/2007 Computer Science Seite 85 Mögliche Fälle: a) x ist ein Blatt => einfaches Löschen b) x hat einen leeren linken oder rechten Teilbaum => Ersetze x durch die Wurzel des anderen Teilbaums c) x hat nichtleeren linken und rechten Teilbaum => Ersetze x durch den kleinsten Wert seines rechten Teilbaumes um die Sortierung beizubehalten. Dies kann ein Umhängen nach sich ziehen. WS 2006/2007 Computer Science Seite 86 43 Wir verwenden zwei Methoden zum Löschen: loesche1 für Fälle a und b, d.h. Knoten hat höchstens 1 Kind loesche2 für Fall c, d.h. Knoten hat zwei Kinder void loescheKnoten(Knoten k){ if (k.getLinks() == null || k.getRechts() == null) loesche1(k); else loesche2(k); } WS 2006/2007 Computer Science Seite 87 Beim Löschen zeigt es sich als sinnvoll, die Knoten für Binäre Bäume noch durch eine weitere Referenz zu ergänzen: Jeder Knoten erhält neben den Referenzen auf den linken und rechten Teilbaum auch noch eine Referenz nach oben auf den Elternknoten. public class Knoten private private private private { int inhalt; Knoten links; Knoten rechts; Knoten oben; // Konstruktoren //get- und set-Methodedn } WS 2006/2007 Computer Science Seite 88 44 void loesche1(Knoten k) {//k hat max. 1 Kind //Bestimme Kind von k Knoten enkel; if (k.getLinks() == null) enkel = k.getRechts(); else enkel = k.getLinks(); if (k == wurzel){ wurzel = enkel; return; } //Ab hier klar: Elternknoten von k existiert Knoten eltern = k.getOben(); // Verbinde Elternknoten zum Enkel if (eltern.getLinks() == k) eltern.setLinks(enkel); else eltern.setRechts(enkel); //Verbinde Enkel zum Elternknoten if (enkel != null) enkel.setOben(eltern); } } WS 2006/2007 Computer Science Seite 89 Zum Löschen eines Knotens mit zwei Kindern, muss dieser durch den kleinsten Wert seines rechten Teilbaumes ersetzt werden. Daher muss eine Methode zum Bestimmen des Minimums definiert werden: Knoten sucheMin (Knoten k){ if (k == null) return k; while (k.getLinks() != null) k = k.getLinks(); return k; } D.h. das kleinste Element eines binären Suchbaumes findet man, indem man dem linken Teilbaum Pointer solange folgt, bis ein Knoten mit leerem linken Teilbaum angetroffen wird. WS 2006/2007 Computer Science Seite 90 45 Implementierung des Löschens eines Knotens mit zwei Kindern void loesche2(Knoten k) {//k hat genau 2 Kinder Knoten min = sucheMin(k.getRechts()); //Kopiere Inhalt nach oben k.setInhalt(min.getInhalt()); loesche1(min); } WS 2006/2007 Computer Science Seite 91 Bemerkung: Der Zeitbedarf für die Operationen Suchen, Einfügen und Löschen ist im Durchschnitt O(log2n). WS 2006/2007 Computer Science Seite 92 46 Behandlung von Duplikaten in Binären Suchbäumen Es gibt verschiedene Möglichkeiten Duplikate in einen binären Suchbaum einzufügen. Eine Möglichkeit ist, Duplikate der Wurzel stets zur neuen Wurzel des linken Teilbaumes zu machen: Die Methode rekEinfuegen verändert sich dann wie folgt: WS 2006/2007 Computer Science Seite 93 void rekEinfuegen (int e, Knoten k){ // Falls Duplikate eingefuegt werden if (e == k.getInhalt()){ Knoten neu = new Knoten (e); if (k.getLinks() != null) k.getLinks().setOben(neu); neu.setLinks(k.getLinks()); k.setLinks(neu); neu.setOben(k); return; } // Ende Duplikate einfuegen if (e < k.inhalt) if (k.getLinks() == null) k.setLinks (new Knoten (e)); else rekEinfuegen (e,k.getLinks()); else if (k.getRechts() == null) k.setRechts(new Knoten (e)); else rekEinfuegen (e,k.getRechts()); } WS 2006/2007 Computer Science Seite 94 47 Beim Löschen muss der zu löschende Knoten durch die Liste aller Duplikate des kleinsten Knotens des rechten Teilbaumes ersetzt werden. Den Inhalt des ersten Knotens dieser Liste kopiert man in den zu löschenden Knoten, die Restliste fügt man zwischen diesen und seinem linken Kind ein. In einem zweiten Schritt muss dann der unten übrig gebliebene oberste Knoten aus der vormaligen Liste von Duplikaten gelöscht werden. Dies geschieht mit der Methode loesche1. WS 2006/2007 Computer Science Seite 95 Implementierung des Löschens eines Knotens mit zwei Kindern falls Duplikate im Minimum vorhanden void loesche2(Knoten k) { //k hat genau 2 Kinder Knoten min = sucheMin(k.getRechts()); // Beginn: Beruecksichtigung von Duplikaten min.setLinks(k.getLinks()); if (min.getLinks() != null) min.getLinks().setOben(min); while (min.getInhalt() == min.getOben().getInhalt()) min = min.getOben(); k.setLinks(min.getLinks()); if (k.getLinks() != null) k.getLinks().setOben(k); min.setLnks(null); // Ende Duplikate //Kopiere Inhalt nach oben k.setInhalt(min.getInhalt()); loesche1(min); } WS 2006/2007 Computer Science Seite 96 48 Balancierte Bäume Damit auch im Worst Case ein geringer Zeitbedarf für die drei Operationen Suchen, Einfügen und Löschen entsteht, eignen sich besonders gut Balancierte Bäume als Datenstruktur. Balanciert: • Alle Blattknoten haben gleiche Tiefe • Alle Operationen haben im Worst Case Zeitbedarf von O(log n). Beispiele für Balancierte Bäume sind AVL-Bäume, 2-3 Bäume, B-Bäume. WS 2006/2007 Computer Science Seite 97 AVL-Bäume Ein binärer Suchbaum heißt AVL-Baum, wenn sich für jeden Knoten des Baums die Höhen seiner Unterbäume höchstens um 1 unterscheiden. Dazu wird jedem Knoten eine zusätzliche Komponente b hinzugefügt. b heißt der Balancefaktor des Knotens und gibt die Differenz der Höhe des rechten Unterbaums und der Höhe des linken Unterbaums an. Also gilt für AVL-Bäume b ∈{−1,0,1} WS 2006/2007 Computer Science Seite 98 49 Einfügen in einen AVL- Baum Fügt man ein Element in einen AVL-Baum ein, so kann es passieren, dass der Balancefaktor eines Teilbaums ungleich –1, 0, 1 ist. Dann läuft man an der Einfügestelle den Suchpfad entlang zur Wurzel zurück und prüft an jedem Knoten, ob die Höhendifferenz zwischen linkem und rechtem Teilbaum noch innerhalb der vorgeschriebenen Grenzen liegt. Falls nein, dann führt man Rotation bzw. Doppelrotation durch. WS 2006/2007 Computer Science Seite 99 2-3 Bäume Wesentliches Merkmal: • Alle Pfade von der Wurzel zu einem Blatt sind gleich lang (Balance) Weiterhin gilt • Echte Informationen stehen nur an den Blättern • Alle inneren Knoten haben 2 oder 3 Kinder • Sortierung wie bei binären Suchbäumen von links nach rechts WS 2006/2007 Computer Science Seite 100 50 Abbildung eines 2-3 Baumes Inneren Knoten enthalten jeweils den Wert des kleinsten Knotens im zweiten Teilbaum (klz) und ggf. den Wert des kleinsten Knotens im dritten Teilbaum (kld). Für die Höhe H eines 2-3 Baumes gilt für die Anzahl N der Blätter: 2 H ≤ N ≤ 3H also log 3 N ≤ H ≤ log 2 N . Damit gilt: H ist O(log N), zur Basis 2 oder 3. WS 2006/2007 Computer Science Seite 101 Suchverfahren im 2-3 Baum Am inneren Knoten: Falls x < klz, suche im 1. Teilbaum Falls klz ≤ x < kld oder kld nicht vorhanden, suche im 2. Teilbaum Falls x ≥ kld, suche im 3. Teilbaum Falls 3. Teilbaum nicht vorhanden oder x < kld, suche im 2. Teilbaum WS 2006/2007 Computer Science Seite 102 51 Einfügen in 2-3 Baum Zunächst Platz für x suchen. Dann • Wenn innerer Knoten k auf vorletzter Ebene nur 2 Kinder hatte, x geeignet einfügen (dann 3 Kinder). Daher ggf. inneren Knoten anpassen. • Wenn innerer Knoten schon 3 Kinder hatte, diesen Knoten unter Anpassung der Verwaltungsfunktion in k, k`spalten. Kleinere Kinder zu k, größere zu k`. Falls Elternknoten von k auch schon drei Kinder, geht die Spaltung nach oben weiter. • Sonderfall: Hatte die Wurzel bisher schon 3 Unterbäume, so werden Pfade verlängert. Es geschieht kein Ausgleich zwischen Nachbarn. WS 2006/2007 Computer Science Seite 103 Entfernen aus 2-3 Baum Hat ein innerer Knoten k nach Entfernen nur noch 1 Kind, ein benachbarter Knoten jedoch 3 Kinder, dann: Ausgleich mit Nachbarknoten: erst von links Kind holen, falls nicht möglich, dann von rechts. Falls dies nicht möglich ist, folgt Entfernung nach oben weiter, ggf. muss die Wurzel gelöscht werden. WS 2006/2007 Computer Science Seite 104 52 Allgemeine B-Bäume Sind eine Verallgemeinerung von 2-3 Bäumen. • Sie haben die folgenden Eigenschaften: • Alle Pfade von der Wurzel zu einem Blatt sind gleich lang. • Echte Informationen nur an den Blättern. • Sortierung von links nach rechts. • Zahl der Verweise bei inneren Knoten sind k,…, 2k-1 mit k ≥ 2. • Zahl der Verweise bei Wurzel 2,…,2k-1. Ein 2-3 Baum ist ein B-Baum für k = 2 . WS 2006/2007 Computer Science Seite 105 Entfernen aus B- Baum 1. Ausgleich unter Nachbarn, wenn Unterlauf < k und ein Nachbar abgeben kann (d.h. > k). 2. Verschmelzung, wenn Unterlauf und kein Nachbar abgeben kann (z.B. k-1 und Nachbar hat k Kinder), dann Verschmelzung zu 2k-1 Kinder. 3. Falls damit auf nächst höherer Stufe Unterlauf, rekursiv weiter. 4. Hat Wurzel dann nur 1 Verweis, dann Wurzel löschen. WS 2006/2007 Computer Science Seite 106 53 Einfügen in B- Baum 1. Spaltung in zwei Blöcke, falls Überlauf > 2k-1. 2. Falls dadurch auf nächst höherer Stufe Überlauf, rekursiv weiter. 3. Bei Spaltung der Wurzel, neue Wurzel mit zwei Verweisen anlegen. Bei B-Bäumen, insbesondere bei sehr großem k, sind die Weglängen zu den Blättern von der Wurzel sehr gering. WS 2006/2007 Computer Science Seite 107 Zusammenfassung • Wörterbücher sind Datenstrukturen auf denen man die Operationen Suchen, Einfügen und Entfernen von Elementen ausführen kann. • Implementierungsmöglichkeiten durch Listen, Hashtabellen, Bäume. • Beim Hashing unterscheidet man zwischen Offenem- und Geschlossenem Hashing. • Binäre Suchbäume haben für alle drei Operationen einen Zeitbedarf von O(log2n) im Durchschnitt. Worst Case liegt bei O(n). • Balancierte Bäume, wie 2-3 Bäume, B-Bäume haben ein Zeitbedarf von O(log n) im Worst Case. Sie sind für den Zugriff großer Datenmengen auf externen Datenträgern geeignet. WS 2006/2007 Computer Science Seite 108 54 Sortierverfahren Gegeben: Unsortierte Folge a1,…,an von Elementen. Gesucht: Sortierte Folge mit ai ≤ ai+1 . Wir werden die verschiedenen Sortieralgorithmen auf Arrays betrachten, d.h. das Problem lautet: Sei a ein Array mit a[0],...,a[n-1]. Gesucht wird ein sortiertes Array mit a[i] ≤ a[i+1]; i = 0,...,n-2 WS 2006/2007 Computer Science Seite 109 Eine Lösungsmöglichkeit: Elemente nach und nach in binären Suchbaum bzw. in 2-3 Baum eintragen. Dies geschieht für 2-3 Bäume im Durchschnitt und Worst Case in O(n log n) Schritten und für binäre Suchbäume im Mittel in O(n log n) Schritten. Gesucht: bessere Sortierverfahren. Es gibt eine ganze Reihe von Sortieralgorithmen. Wir werden den BubbleSort, QuickSort, MergeSort sowie den HeapSort betrachten. WS 2006/2007 Computer Science Seite 110 55 Bubble-Sort Bubble-Sort ist ein elementares Sortierverfahren. Idee des Bubble-Sort Bubble-Sort bringt in mehreren Durchläufen jeweils das kleinste Element des Restarrays an die erste Position. Im ersten Durchlauf ist das Restarray gleich dem ganzen Array. Im zweiten Durchlauf fehlt gegenüber dem ersten Durchlauf das erste Element. In jedem Durchlauf bringt man das kleinste Element nach vorne, in dem vom letzten Element her jedes Element mit seinem Vorgänger verglichen wird. Falls es kleiner als sein Vorgänger ist, wird es vertauscht. Der Algorithmus kann abgebrochen werden, wenn nichts mehr vertauscht werden muss. WS 2006/2007 Computer Science Seite 111 Der Algorithmus kann in Pseudocode wie folgt formuliert werden: algorithm BubbleSort(a[]){ b ← true; j ← a.length; while(b){ //true b ← false; for (i = a.length-1;i > a.length-j;i--) if (a[i] < a[i-1]){ a[i] ↔ a[i-1]; b ← true; } j--; } } b gibt an, ob noch etwas vertauscht wird. Falls false, wird der Algorithmus abgebrochen. WS 2006/2007 Computer Science Seite 112 56 Anmerkungen zum Bubble-Sort Bubble-Sort ist ein recht langsames Sortierverfahren. Für den Zeitaufwand gilt: n −1 (n-1) + (n-2) + ...+ 1 = ∑ i =1 i = n ( n − 1) = O (n 2 ) 2 WS 2006/2007 Computer Science Seite 113 „Divide and Conquer“ Algorithmen Divide and Conquer Algorithmen beschreiben folgende Problemlösestrategie • Zerlege das Problem P in die Teilprobleme P1,...,Pn . • Löse das Problem rekursiv für jede Teilmenge. • Setze die Lösung L von P als Kombination der Lösungen L1,..., Ln der Teilmengen zusammen. Ein Vertreter dieser Algorithmen ist das Sortierverfahren QuickSort. WS 2006/2007 Computer Science Seite 114 57 QuickSort QuickSort wurde 1962 von dem britischen Informatiker C.A.R.Hoare entwickelt. Damals waren noch keine schnellen Sortierverfahren bekannt. QuickSort ist ein rekursiver Algorithmus. Rekursive Algorithmen galten früher als ineffizient. WS 2006/2007 Computer Science Seite 115 Der Algorithmus QuickSort arbeitet wie folgt: Gegeben sei das unsortierte Array a[0],…,a[n-1] der Länge n. Divide: Teile die Elemente des Arrays a[l],…,a[r] in zwei Teilarrays a[l],…,a[k] und a[k+1],..,a[r], so dass gilt a[i] ≤ a[j] für alle l ≤ i ≤ k und k+1 ≤ j ≤ r . (Im ersten Schritt ist l = 0 und r = n-1) Diesen Schritt nennt man Partitionierung. Conquer: Sortiere die beiden Teilarrays rekursiv indem QuickSort für die Teilarrays aufgerufen wird. WS 2006/2007 Computer Science Seite 116 58 Wie erfolgt die Partitionierung? Zu Beginn der Partitionierung wird ein Index p ausgewählt. Der Wert des Arrays an der Stelle p, d.h. a[p] bezeichnet man als Pivot Element. Von links her wird gesucht, bis erstes Element a[l] ≥ Pivot gefunden, von rechts her wird gesucht, bis erstes Element a[r] ≤ Pivot gefunden. Dann werden diese Elemente vertauscht, wobei l um 1 erhöht und r um 1 erniedrigt wird. Dieser Vorgang wird wiederholt. Das Verfahren bricht ab, falls l > r wird. Bemerkung zur Wahl des Pivot-Elementes Als Pivot Element wählt man meist ein Element in der Mitte des Arrays, d.h. Pivot = a[(l+r)/2]. WS 2006/2007 Computer Science Seite 117 Der Algorithmus QuickSort in Pseudocode algorithm quickSort (a[], begin, end) { if (begin >= end) return; pivot ← a[(begin+end)/2]; l ← begin; r ← end; Partitionierung while(l <= r){ while (a[l] < pivot) l++; while(a[r] > pivot) r--; if (l <= r){ a[l] a[r] ↔ l++; r--; } } quickSort (a, begin, r); quickSort (a, l, end); WS 2006/2007 Computer Science Seite 118 59 Zeitkomplexität von QuickSort Worst Case Aufteilung der Länge n durchweg in Längen n-1 und 1. Dann hat der Algorithmus die Komplexität O(n2). Die Aufteilung erfolgt n mal und n Elemente werden durchsucht. Best Case und Average Case: Aufteilung in Längen n/2 und n/2. Dann gilt für die Zeitkomplexität O(n log n). Die Aufteilung erfolgt log n mal und n Elemente werden durchsucht. WS 2006/2007 Computer Science Seite 119 MergeSort MergeSort ist ein weiterer Divide and Conquer Algorithmus. MergeSort teilt die ursprüngliche Menge der Datensätze in zwei Hälften auf. Diese werden sortiert und dann zusammengemischt. Dabei vergleicht man immer wieder die vordersten Elemente der sortierten Hälften und entnimmt das kleinste der beiden. Auf diese Weise verschmelzen (merge) die zwei geordneten Listen zu einer gemeinsamen geordneten Liste, die alle Elemente der ursprünglichen zwei Listen enthält. WS 2006/2007 Computer Science Seite 120 60 Der Algorithmus MergeSort arbeitet wie folgt • Divide, d.h. teile Datensätze in zwei Teilmengen mit jeweils n/2 Elemente • Conquer durch Sortierung der beiden Teilmengen rekursiv durch Aufruf von MergeSort • Merge, d.h. mische beide sortierten Teilmengen. WS 2006/2007 Computer Science Seite 121 Der Algorithmus MergeSort in Pseudocode algorithm mergeSort (a[],l, r) { if (l < r) { m ←(l+r+1)/2; mergeSort(a[],l,m-1); mergeSort(a[],m,r); merge(a[],l,r,m); } } WS 2006/2007 Computer Science Seite 122 61 Der Teilalgorithmus Merge stellt sich wie folgt dar: merge (a[], l, r,m){ //Lege Hilfsarray temp der Laenge r-l+1 an for (i=0, j=l, k=m; i < temp.length; i++) if ((k > r) || ((j < m) && (a[j] < a[k]))){ temp[i] ← a[j]; j++; } else { temp[i] ← a[k]; k++; } for (i=0; i < temp.length; i++) a[l+i] ←temp[i]; } WS 2006/2007 Computer Science Seite 123 Zeitkomplexität von MergeSort Best Case und Average Case und Worst Case: Aufteilung in Längen n/2 und n/2. Dann gilt für die Zeitkomplexität O(n log n). Die Aufteilung erfolgt log n mal und n Elemente werden durchsucht. Da die Aufteilung immer optimal ist (im Gegensatz zu Quicksort), gilt die Zeitkomplexität auch im Worst Case. WS 2006/2007 Computer Science Seite 124 62 Bemerkung zum MergeSort Der Algorithmus benötigt ein zusätzliches Hilfsarray, in dem die Elemente gespeichert werden. D.h. es wird zusätzlicher Speicherplatz benötigt. WS 2006/2007 Computer Science Seite 125 HeapSort HeapSort gilt als sehr guter Sortieralgorithmus. Er garantiert auch im Worst Case ein Zeitverhalten von O (n log n), d.h. entspricht hier im Zeitverhalten dem Algorithmus Merge-Sort. Allerdings benötigt HeapSort keinen weiteren Hilfsarray, so dass HeapSort zum Sortieren erheblich weniger Speicher als MergeSort benötigt. WS 2006/2007 Computer Science Seite 126 63 Zur Erinnerung Ein Array a heißt Heap, wenn es ein vollständiger Baum ist, für den gilt a[i] >= a[2i+1] und a[i] >= a[2(i+1)] Ein Heap ist also ein Binärbaum mit den beiden Eigenschaften 1. Form 2. Ordnung: Entlang jedes Pfades von einem Knoten zur Wurzel sind die Knoteninhalte aufsteigend sortiert WS 2006/2007 Computer Science Seite 127 Bevor wir HeapSort definieren, werden wir zunächst zwei fundamentale Heap Algorithmen behandeln. Dies sind insert und deleteMax. WS 2006/2007 Computer Science Seite 128 64 Einfügen eines Elementes in einen Heap Damit ein Heap vollständig bleibt, gibt es nur 1 mögliche Position, an der ein neues Element eingefügt werden kann. Damit kann aber die Ordnungseigenschaft verletzt werden. Sie muss dann wieder hergestellt werden. Insert: Knoten wird mit Elternknoten verglichen und ggf. mit diesem vertauscht. Dieser Prozess setzt sich nach oben fort, bis Ordnungseigenschaft für Heap wieder hergestellt. Diesen Algorithmus nennt man auch UpHeap. WS 2006/2007 Computer Science Seite 129 Algorithmus zum Einfügen eines Elementes in einen Heap algorithm UpHeap(heapo[ ], e) { //Kopiere Array heapo in ein Array heapt der Laenge //heapo.length+1 i←heapo.length; heapt[i] ←e; while (i > 0) { hi ← (i-1)/2; if (heapt[i] > heapt[hi]) { heapt[hi] ↔ heapt[i]; i←hi; } else break; } return heapt; } WS 2006/2007 Computer Science Seite 130 65 Löschen des größten Elementes eines Heaps Das größte Element eines Heaps ist an der Wurzel. Um die Wurzel zu entfernen, ersetzt man diese durch das am weitesten rechts stehende Blatt der untersten Ebene. So bleibt die Form-Eigenschaft erhalten. Um Ordnung zu erhalten muss das neue Element an der Wurzel ggf. nach unten wandern. Dabei wird es jeweils mit dem größten KindKnoten vertauscht bis Platz gefunden. Also: 1. Letztes Element in Wurzel kopieren 2. Element nach unten wandern lassen Diesen Algorithmus nennt man auch DownHeap. WS 2006/2007 Computer Science Seite 131 algorithm DownHeap (heapo[ ]) { l = heapo.length-1; // Kopiere l-1 Elemente von heapo ab Pos. 1 //in array heapt ab Pos. 1 heapt[0] ← heapo[l]; //letztes Blatt wird an Wurzel //von heapt gestellt i ←0; hi ←1; while (hi < l) { // linker oder rechter Teilbaum if (((hi+1) < l) && (heapt[hi] < heapt[hi+1])) hi++; if (heapt[i] < heapt[hi]){ // Muss getauscht werden? heapt[hi]↔ heapt[i]; // Vertausche beide Elemente i ← hi; hi ← 2*hi+1; } else break; } return heapt; } WS 2006/2007 Computer Science Seite 132 66 Ablauf des Algorithmus HeapSort Sei a ein unsortiertes Array (noch kein Heap) der Länge n+1. 1.Phase: „Heap bauen“ a[ n − 1 2 +1],…,a[n] ist ein Teilheap, da diese Knoten keine Kinder haben. Erweitere diesen Teilheap zu einem Teilheap a[ n − 1 2 ],…,a[n]. Das Ergebnis ist der Heap a[0],…,a[n]. 2. Phase: Entferne Maximum und schreibe dies beginnend bei a[n] in das Array. WS 2006/2007 Computer Science Seite 133 Der Algorithmus HeapSort algorithm HeapSort (a[]) { for (i = (a.length-2)/2; i >= 0 ; i--) ReHeap(a,i,a.length-1); for (i = (a.length-1); i >= 1;i--) { a[0] ↔ a[i]; ReHeap(a,0,i-1); } } WS 2006/2007 Computer Science Seite 134 67 algorithm ReHeap (a[ ],l,r) { i ←l; while (2*i+1 <= r) { if ((2*i+1) < r) { //Knoten hat zwei Kinder if (a[2*i+1] > a[2*(i+1)]) //Welches ist das groesste Kind? hi ←2*i + 1; else hi ←2*(i+1); } else hi ←2*i+1; //Knoten hatte nur ein Kind if (a[i] < a[hi]){ a[hi]↔ a[i]; // Vertauschen i ←hi; } else i ←r; } } WS 2006/2007 Computer Science Seite 135 DistributionSort DistributionSort ist ein Sortieralgorithmus, der Daten auf Schlüsselwerten sortiert. Der Sortierschlüssel kann z.B. eine Ziffernfolge, eine Bytefolge oder eine Zeichenfolge eines festen Formats sein. Beispiel für Schlüssel sind fünfstellige Postleitzahlen,… Verfahren: Die zu sortierenden Datensätze werden anhand einer Ziffernposition in Körbe verteilt und wieder zusammengetragen. Zuerst wird anhand der letzten Position verteilt, danach nach der nächsten Position. Die Datensätze sind sortiert, wenn die erste Position der Ziffernfolge bei der Verteilung erreicht wurde. WS 2006/2007 Computer Science Seite 136 68 Beispiel für DistributionSort Es seien Briefe nach ihren Empfänger – Wohnorten zu sortieren. Der Sortierschlüssel sei die 5-stellige Postleitzahl. Für jede der 10 Ziffern 0,1,…,9 wird ein Korb zur Verfügung gestellt. Sortiert wird dann wie folgt: 1. Verteile alle Briefe auf die 10 Körbe anhand der letzten Ziffer. 2. Zusammenschieben unter Beibehaltung der bisherigen Ordnung 3. Verteilung aller Briefe anhand der vorletzten Ziffer 4. Zusammenschieben…..bis Position 1 erreicht. WS 2006/2007 Computer Science Seite 137 Externes Sortieren Ist die Anzahl der zu sortierenden Datensätze so groß, dass nicht alle Daten im Hauptspeicher des Rechners gehalten werden können, so spricht man von externer Sortierung. Das Problem der externen Sortierung lässt sich auf die interne Sortierung (Sortierung allein im Hauptspeicher) zurückführen. Verfahren: Sei D eine große zu sortierende Datei, die nicht im Hauptspeicher gehalten werden kann. Teile D in n Teile D1,…,Dn, die jeweils klein genug sind, um intern sortiert zu werden. Diese werden der Reihe nach sortiert. Die sortierten Dateien D´1,…,D´n werden zu einer sortierten Datei D´zusammengefügt. Dies geschieht durch Mischen. WS 2006/2007 Computer Science Seite 138 69 Zusammenfassung Sortieralgorithmen • Bei den Sortieralgorithmen unterscheidet man zunächst zwischen externem und internem Sortieren. • Die internen Sortierverfahren unterteilen sich in einfache und schnelle Sortierverfahren. • Zu den einfachen Sortierverfahren zählt u.a. Bubble-Sort mit einem Aufwand von O(n2) • Zu den schnellen Sortierverfahren gehören die rekursiven Algorithmen QuickSort und MergeSort sowie der Algorithmus HeapSort. • Rekursive Algorithmen ‚lohnen sich erst‘, wenn die Datenmenge ausreichend groß ist. WS 2006/2007 Computer Science Seite 139 Graphen und Graph-Algorithmen Einleitung Ein Graph stellt eine Menge von Objekten zusammen mit einer Beziehung auf diesen Objekten dar. Beispiel: Objekte: Personen; Beziehungen: Person A kennt Person B. Die Bezeichnung Graph stammt von der üblichen ‚graphischen‘ Darstellung: Objekte werden als Knoten, Beziehungen als Kanten dargestellt. Eine Beziehung wird im allgemeinen durch eine gerichtete Kante (Pfeil) dargestellt. Falls die Beziehung symmetrisch ist, wird sie durch eine ungerichtete Kante dargestellt. WS 2006/2007 Computer Science Seite 140 70 Gerichtete Graphen Definition Ein gerichteter Graph ist ein Paar G = (V,E), wobei gilt: (i) V ist eine endliche, nichtleerer Menge (die Elemente werden Knoten genannt), (ii) E ⊆ V x V (die Elemente heißen Kanten). Eine Kante ist also ein Paar von Knoten. Die Kante e = (v,w) wird graphisch wie folgt dargestellt: v →w WS 2006/2007 Computer Science Seite 141 Speicher- Darstellung von Graphen Es gibt verschiedene Datenstrukturen zur Darstellung von Graphen. Die Art der auszuführenden Operationen bestimmt die Wahl einer Datenstruktur für einen bestimmten Algorithmus. Zwei gängige Datenstrukturen sind die Adjazenzmatrix und die Adjazenzliste. Sei n = |V| die Anzahl der Knoten und sei V = {v1,...,vn}. WS 2006/2007 Computer Science Seite 142 71 Die Adjazenzmatrix Die Adjazenzmatrix A = (aij) für den Graphen G = (V,E) ist eine (nxn)-Matrix mit { 1 falls (vi,vj) ∈ E 0 sonst aij = WS 2006/2007 Computer Science Seite 143 Die Adjazenzlisten In einer Adjanzenzliste wird für jeden Knoten eine Liste seiner Nachbarknoten verwaltet. Über einen Array der Länge n = |V| ist jede Liste direkt zugänglich. WS 2006/2007 Computer Science Seite 144 72 Durchlauf von Graphen Problem: Systematisch sollen alle Knoten eine Graphen G aufgesucht werden, d.h. der Graph soll systematisch durchlaufen werden. Hierbei muss darauf geachtet werden, dass man nicht in Endlosschleifen gerät, wenn der Graph Zyklen hat. Daher markiert man bereits besuchte Knoten. Es gibt zwei wesentliche Arten, Graphen zu durchlaufen. 1. Breitendurchlauf (breadth-first-traversal) 2. Tiefendurchlauf (depth-first-traversal) Beide Verfahren lassen sich mit Hilfe von Bäumen erklären. WS 2006/2007 Computer Science Seite 145 Definition Die Expansion X(v) eines Graphen G in einem Knoten v ist ein Baum, der wie folgt definiert ist: 1. Falls v keine Nachfolger hat, so ist X(v) der Baum, der nur aus dem Knoten v besteht. 2. Falls v1,...,vk die Nachfolger von v sind, so ist X(v) der Baum (v,X(v1),...,X(vk)). WS 2006/2007 Computer Science Seite 146 73 Ein Tiefendurchlauf entspricht einem Preorder-Durchlauf der Expansion von G, der jeweils in einem bereits besuchten Knoten von G abgebrochen wird. Ein Breitendurchlauf besucht die Knoten der Expansion ebenenweise, d.h. zuerst die Wurzel, danach alle Knoten auf Level 1, danach auf Level 2,.... Bei schon besuchten Knoten wird abgebrochen. WS 2006/2007 Computer Science Seite 147 Der Algorithmus zum Tiefendurchlauf Sei |V| = n. algorithm dfs (v) // Tiefendurchlauf vom Knoten v aus if (v wurde noch nicht besucht){ verarbeite v; markiere v als besucht; for (jedem Nachfolger vi von v) dfs (vi) } WS 2006/2007 Computer Science Seite 148 74 Bemerkung zum Algorithmus zum Tiefendurchlauf • Ob v bereits besucht wurde, wird in einem zusätzlichem Array mit boolean-Werten der Länge n vermerkt. Vor Ausführung des Algorithmus besteht das Array aus false Werten. Falls ein Knoten besucht wurde, wird die entsprechende Position im Array auf true gesetzt. • Da im allgemeinen nicht alle Knoten von einem Wurzelknoten erreichbar sind, muss der Algorithmus dfs (v) in folgende Schleife eingebunden werden: for (i = 1; i <= n; i++) dfs(v) WS 2006/2007 Computer Science Seite 149 Der Algorithmus zum Breitendurchlauf algorithm bfs (v) // Breitendurchlauf vom Knoten v aus fuege in die leere Schlange q den Knoten v ein; markiere v als besucht; while (q enthaelt Elemente){ w ← erste Element von q; entferne erstes Element aus q; verarbeite w; for (jedem Nachfolger wi von w) if (wi wurde noch nicht besucht){ haenge wi an q an; markiere wi als besucht; } } WS 2006/2007 Computer Science Seite 150 75 Bemerkung zum Algorithmus zum Breitendurchlauf • Zur Realisierung des Breitendurchlaufs verwendet man die Datenstruktur ‚Schlange‘ (FiFo). • Da im allgemeinen nicht alle Knoten von einem Wurzelknoten erreichbar sind, muss der Algorithmus bfs (v) in folgende Schleife eingebunden werden: for (i = 1; i <= n; i++) if (v wurde noch nicht besucht) bfs(v) WS 2006/2007 Computer Science Seite 151 Bestimmung kürzester Wege von einem Knoten zu allen anderen Knoten Gegeben sei ein gerichteter Graph, dessen Kanten mit positiven reellen Zahlen (Kosten) beschriftet sind. Problem: Von einem beliebigen Knoten k (Quelle) finde Kosten des jeweils billigsten Pfades zu jedem anderen Knoten. Unter Kosten des Pfades versteht man die Kosten seiner Kanten. Ein Algorithmus zur Lösung dieses Problems ist der Algorithmus von Dijkstra. WS 2006/2007 Computer Science Seite 152 76 Algorithmus von Dijkstra Voraussetzungen: Sei n = |V| die Anzahl der Knoten im Graph. Sei Ko ein zweidimensionales Array mit reellen Werten: Ko: array [0…n-1][0…n-1] Eintrag Ko[i,j] ist Kosten der Kante i->j, falls diese existiert, sonst maxreal. Bi ist ein array der Länge n mit reellen Werten und enthält die bisher geringsten Kosten für Pfade von der Quelle zu den Knoten. WS 2006/2007 Computer Science Seite 153 algorithm Dijkstra{ M ← {0}; Bi[0]←0; for (i = 1; i<= n-1;i++) Bi[i] ← Ko[0][i]; //Initialisierung for (i = 0; i <=n-2; i++){ fuege denjenigen Knoten w ∈ V \ M zu M hinzu, bei dem Bi[w] minimal ist; for (alle Knoten v ∈ V \ M ) Bi[v]←min(Bi[v], Bi[w] + Ko[w,v]); } } WS 2006/2007 Computer Science Seite 154 77 Bemerkung zum Dijkstra-Algorithmus • Die Zeitkomplexität beträt O(|V|2). • Werden die Bi[v] in einer Prioritätsschlange verwaltet, so beträgt die Zeitkomplexität O(|E|*log(|V|). WS 2006/2007 Computer Science Seite 155 Bestimmung kürzester Wege zwischen allen Paaren von Knoten Verfahren von Floyd Idee: In Matrix A werden Kosten des bis jetzt billigsten Pfades von i nach j für alle i, j ∈ V gespeichert. Die Zeitkomplexität beträgt O(|V|3). WS 2006/2007 Computer Science Seite 156 78 algorithm Floyd{ for (i = 0; i<= n-1;i++) for (j = 0; j<= n-1;j++) A[i][j] ← Ko[i][j]; //Initialisierung for (i = 0; i<= n-1;i++) A[i][i] ← 0; for (k = 0; k <=n-1; k++) for (i = 0; i <=n-1; i++) for (j= 0; j<=n-1; j++) if A[i][k] + A [k][j] < A[i][j] A[i][j] ← A[i][k] + A [k][j] } WS 2006/2007 Computer Science Seite 157 Verfahren von Warshall Der Algorithmus von Warshall gibt an, ob ein Pfad im Graph existiert. A ist ein boolesche Matrix. Algorithmus läuft wie bei Floyd, außer innerste Schleife: for (j= 0; j<=n-1; j++) if ( A[i][j] = false) A[i][j] ←(A[i][k] AND A[k][j]) WS 2006/2007 Computer Science Seite 158 79 Einschub Die Datenstruktur „Mischwald“. Unter einem Mischwald versteht man eine Datenstruktur, für die 2 Operationen definiert ist: • Vereinigen zweier disjunkter Mengen (diese werden Komponenten genannt) in Zeit O(1). Diese Operation nennt man merge. • Feststellen, zu welcher Menge ein Element gehört in Zeit O(log(n)). Die Datenstruktur Mischwald ist eine Hilfstruktur für Graphen. WS 2006/2007 Computer Science Seite 159 Ungerichtete Graphen Definitionen Ein Graph G = (V,E) heißt ungerichteter Graph : auch (w,v) ⇔ für alle (v,w) ∈ E ist ∈ E. G‘ = (V‘,E‘) heißt Subgraph von G = (V,E) : G heißt verbunden : ⇔ V‘ ⊆ V, E‘ ⊆ (V‘ x V‘) ∩ E. ⇔ zu je zwei Knoten x, y existiert ein Pfad x,…,y. Ein Spannbaum für G ist ein verbundener Subgraph Sp = (V,E‘) von G ohne Zyklen, wobei E‘ eine Teilmenge von E ist. WS 2006/2007 Computer Science Seite 160 80 Sei G = (V,E) verbundener Graph mit Kosten ko[i,j] für alle Kanten (i,j) ∈ E. Die Kosten eines Spannbaums sind die Summe der Kosten über alle e ∈ E‘. Ziel: Zu einem verbundenen Graphen G mit Kantenbewertung wird ein Spannbaum mit minimalen Kosten gesucht. Diesen nennt man minimalen Spannbaum. WS 2006/2007 Computer Science Seite 161 Eigenschaften minimaler Spannbäume Lemma: Sei {U,W} eine Zerlegung der Knotenmenge V für G = (V,E). Sei (u,w) eine Kante in G mit minimalen Kosten unter allen Kanten {(u‘,w‘) | u‘ ∈ U, w‘ ∈W}. Dann gibt es einen minimalen Spannbaum für G, der (u,w) enthält. WS 2006/2007 Computer Science Seite 162 81 Algorithmus von Kruskal Sei G = (V,E) ein verbundener, kantenbewerteter Graph mit n Knoten. Q sei eine Prioritäts-Schlange mit den Kosten der Kanten, wobei die geringsten Kosten höchste Priorität haben. Das Entfernen des Minimums erfolgt mit dem Algorithmus deletemin. P sei die Datenstruktur Mischwald mit den Operationen find und merge. Gesucht: Minimaler Spannbaum für den Graphen G. WS 2006/2007 Computer Science Seite 163 algorithm Kruskal (G){ Initialisiere P so, dass jeder Knoten aus V eine eigene Komponente bildet; SP ← (V, O ); //Initialisierung des Spannbaums Füge alle Kanten aus E bzgl. ihrer Kosten in Q ein; zaehler ← n; while (zaehler > 1){ (Q,(v,w)) a b ← deletemin(Q); ← find(P,v); ← find(P,w); // zu welcher Komponente gehört v? // zu welcher Komponente gehört w? if (a != b){ insert(SP, (v,w)); //fügt Kante (v,w) in Baum SP ein P ← merge (P, a, b); // mische Komponente a und b zaehler ← zaehler – 1; } }} WS 2006/2007 Computer Science Seite 164 82 Bemerkung zum Algorithmus von Kruskal Die Zeitkomplexität ist O(e log e) , wobei e die Anzahl der Kanten ist. WS 2006/2007 Computer Science Seite 165 Teil 2: Grundlagen der Theoretischen Informatik Einführung in die Automatentheorie und formale Sprachen WS 2006/2007 Computer Science Seite 166 83 Grundbegriffe Als Alphabet ∑ bezeichnet man eine nicht-leere Menge. Die Elemente dieser Menge heißen Symbole oder Buchstaben. Die endlich langen Zeichenfolgen, die über ein Alphabet ∑ gebildet werden können, heißen Wörter über ∑ . Für ein gegebenes Alphabet ∑ ∗ bezeichnet ∑ die Menge aller Wörter, die sich durch Hintereinanderschreiben von Symbolen aus ∑ ∗ bilden lassen, d.h. ∑ ist die Menge aller endlichen Folgen von Elementen aus ∑ inkl. der leeren Folge bzw. das leere Wort, das mit ε bezeichnet wird. WS 2006/2007 Computer Science Seite 167 Sei ∑ ∗ ein Alphabet. Dann heißt jede Menge L ⊆ ∑ eine (formale) Sprache über ∑ . WS 2006/2007 Computer Science Seite 168 84 Deterministische endliche Automaten und reguläre Sprachen Definition: Ein deterministischer endlicher (Zustands-) Automat ist ein System A = ( Σ , S , δ , s 0 , F ) . Dabei ist ∑ das Eingabealphabet und S die Zustandsmenge von A, Menge der Endzustände und s0 ∈ S ist der Startzustand, F ⊆ S die δ : S ×Σ → S die Zustandsüberführungs- funktion von A. A heißt deterministisch, da zu einem Zustand und einem Eingabesymbol höchstens ein Folgezustand existieren kann. WS 2006/2007 Computer Science Seite 169 Sei A = (Σ , S , δ , s0 , F ) ein endlicher Automat. Dann wird die erweiterte Zustandsüberführungsfunktion δ ∗ definiert durch δ ∗ : S × Σ∗ → S δ ∗(s,ε ) = s mit für alle s∈S δ ∗ (s, a w) = δ ∗ (δ (s, a),w) , a ∈Σ, w∈Σ∗ WS 2006/2007 Computer Science Seite 170 85 Konfiguration und Konfigurationsübergänge Eine Konfiguration kA eines Automaten A beschreibt den aktuellen Stand der Verarbeitung einer Eingabefolge von A. Eine Konfiguration kann durch ein Paar k = kA = (s,v) mit s∈S und v ∈ Σ∗ beschrieben werden. Ein Konfigurationsübergang von einer Konfiguration k = (s,aw) zu einer Konfiguration k‘ = (s‘,w) kann stattfinden, falls der Zustandsübergang δ (s, a) = s' existiert, wobei s , s '∈ S , w ∈ Σ ∗ , a ∈ Σ. Ein Konfigurationsübergang von k = (s,w) zu k‘ = (s‘,w‘) wird durch (s,w) (s‘,w‘) dargestellt. WS 2006/2007 Computer Science Seite 171 Reguläre Sprachen Definition: Sei A = ( Σ , S , δ , s 0 , F ) ein endlicher Automat. Dann heißt die Sprache L ( A) = {w ∈ Σ ∗ ( s0 , w) ∗ ( s , ε ), s ∈ F } die von A akzeptierte Sprache. * bezeichnet die reflexiv-transitive Hülle der Relation Eine Sprache L ⊆ Σ ∗ heißt regulär, falls es einen endlichen Automaten A gibt, der L akzeptiert, d.h. für den L = L(A) gilt. WS 2006/2007 Computer Science Seite 172 86 Zusammenfassung • In Abhängigkeit ihres aktuellen Zustandes und einer Eingabe gehen endliche Automaten gemäß einer festgelegten deterministischen Zustandsüberführung in einen neuen Zustand über. • Die Abarbeitung eines Eingabewortes geschieht durch einen Prozess, der durch eine Folge von Konfigurationsübergängen formal beschrieben werden kann. • Ein Automat akzeptiert das Eingabewort, wenn er nach seiner vollständigen Abarbeitung einen Endzustand erreicht hat. • Die Menge aller von einem endlichen Automaten akzeptierten Wörtern stellt die von ihm akzeptierte Sprache dar. • Von endlichen Automaten akzeptierte Sprachen heißen reguläre Sprachen. WS 2006/2007 Computer Science Seite 173 Nichtdeterministische endliche Automaten Definition: Ein nichtdeterministischer endlicher Automat ist ein System A = ( Σ , S , δ , S 0 , F ) . Dabei ist ∑ das Eingabealphabet und S die Zustandsmenge von A, S0 ⊆ S ist die Menge der Startzustände, F ⊆ S die Menge der Endzustände und δ ⊆ S ×Σ× S die Zustandsüberführungsrelation von A. δ ist ein Menge von Tripeln (Zustand, Eingabesymbol, Folgezustand) WS 2006/2007 Computer Science Seite 174 87 Ein Konfigurationsübergang von k = (s,aw) zu einer k‘ = (s‘,w), d.h. (s,aw) (s‘,w‘) kann stattfinden, falls (s,a,s‘) ∈ δ , a ∈ Σ, w ∈ Σ ∗ Die von einem nichtdeterministischen Automaten A akzeptierte Sprache L(A) wird definiert durch L ( A) = {w ∈ Σ ∗ ( s 0 , w) ∗ ( s , ε ), s0 ∈ S 0 , s ∈ F } WS 2006/2007 Computer Science Seite 175 Zusammenfassung • Nichtdeterministische endliche Automaten lassen in einem Zustand für eine Eingabe mehrere Folgezustände zu. • Zur Überprüfung der Akzeptanz von Eingabeworten, kann bei sequentieller Abarbeitung das sog. Backtracking nötig sein. • Die parallele Abarbeitung eines Wortes führt alle möglichen Konfigurationsfolgen gleichzeitig aus. • Nichtdeterministische und deterministische endliche Automaten akzeptieren dieselbe Klasse von Sprachen, dies ist die Klasse der regulären Sprachen. WS 2006/2007 Computer Science Seite 176 88 Grammatiken Ein endlicher Automat definiert die Sprache durch Akzeptieren ihrer Wörter. Mit Hilfe von Grammatiken werden Wörter einer Sprache erzeugt. Grammatiken geben Regeln an, mit denen aus einem Startwert alle Wörter der Sprache hergeleitet werden können Zunächst betrachten wir Typ-3 Grammatiken. WS 2006/2007 Computer Science Seite 177 Typ-3 Grammatiken: Rechtslineare- und linkslineare Grammatiken Definition: Eine rechtslineare Grammatik G = ( Σ , N , P , S ) über ∑ besteht aus dem Terminalalphabet ∑ , dem davon disjunkten Nonterminalalphabet N, einer endlichen Relation sowie dem Startsymbol Die Elemente p = (l,r) s ∈ N. P ⊆ N ×(Σ N ∪Σ ∪{ε}) ∈ P heißen Produktion oder Regeln, P heißt die Produktionenmenge. Man schreibt für (l,r) l →r WS 2006/2007 Computer Science Seite 178 89 Ein Ableitungsschritt mit der rechtslinearen Grammatik G = ( Σ , N , P , S ) ist eine Relation definiert durch u A ⇒ u w genau dann, wenn A → w ∈ P , wobei u ∈Σ∗, A∈ N, w∈Σ N ∪Σ ∪{ε }. Eine Ableitung in G läßt sich formalisieren durch die reflexiv transitive Hülle ⇒ ∗ von ⇒ . WS 2006/2007 Computer Science Seite 179 Die von G erzeugte Sprache L(G) ist definiert durch { } L (G ) = w ∈ Σ ∗ S ⇒ ∗ w Zwei Grammatiken G1 und G2 heißen äquivalent, falls sie dieselbe Sprache erzeugen, d.h. falls L(G1) = L(G2) gilt. Eine Sprache über L ⊆ Σ∗ heißt rechtslinear, falls eine rechtslineare Grammatik G über ∑ existiert mit L = L(G). WS 2006/2007 Computer Science Seite 180 90 Definition: Eine linksslineare Grammatik G = ( Σ , N , P , S ) über ∑ besteht aus dem Terminalalphabet ∑ , dem davon disjunkten Nonterminalalphabet N, einer endlichen Relation sowie dem Startsymbol s ∈ N. P ⊆ N ×(N Σ ∪Σ ∪{ε}) Ein Ableitungsschritt mit der linkslinearen Grammatik ist eine Relation definiert durch Au ⇒ wu genau dann, wenn A → w∈ P , wobei u ∈Σ∗, A∈ N, w∈ N Σ ∪Σ ∪{ε}. WS 2006/2007 Computer Science Seite 181 Satz: Zu jeder rechtslinearen Grammatik über ∑ existiert eine äquivalente linklineare Grammatik. Zu jeder linkslinearen Grammatik über ∑ existiert eine äquivalente rechtslineare Grammatik. WS 2006/2007 Computer Science Seite 182 91 Äquivalenz von endlichen Automaten und Typ-3 Grammatiken Satz: Zu jeder Typ-3 Grammatik G über dem Alphabet ∑ existiert ein endlicher Automat A über ∑ mit L(G) = L(A). Zu jedem endlichen Automaten A über dem Alphabet ∑ existiert eine rechtslineare Grammatik G über ∑ mit L(A) = L(G). WS 2006/2007 Computer Science Seite 183 Zusammenfassung • Typ-3 Grammatiken, d.h. rechtslineare und linkslineare Grammatiken erzeugen Wörter, ausgehend von einem Startsymbol, mit Hilfe von Ersetzungsregeln. Dies geschieht buchstabenweise von links nach rechts bzw. von rechts nach links. • Rechtslineare und linkslineare Grammatiken sind untereinander äquivalente erzeugende Konzepte für reguläre Sprachen. • Sie sind äquivalent zu endlichen Automaten. WS 2006/2007 Computer Science Seite 184 92 Endliche Maschinen Ein endlicher Automat akzeptiert eine reguläre Sprache, erzeugt aber keine Ausgabe. Endliche Automaten mit einer Ausgabefunktion bezeichnet man als endliche Maschinen. Zwei Vertreter der endlichen Maschinen sind die sog. Moore- und Mealy-Maschinen. WS 2006/2007 Computer Science Seite 185 Mealy-Machinen Definition: Eine Mealy-Maschine M = ( Σ , ∆ , S , δ , λ , s 0 ) besteht aus dem Eingabealphabet Σ , dem Ausgabealphabet ∆ , der endlichen Zustandsmenge S , der Zustandsüberführungsfunktion δ : S × Σ → S, der Ausgabefunktion λ : S × Σ → ∆ und den Startzustand s ∈ S 0 . Definition: Eine Moore-Maschine M = ( Σ , ∆ , S , δ , λ , s 0 ) besteht aus dem Eingabealphabet Σ , dem Ausgabealphabet ∆ , der endlichen Zustandsmenge S , der Zustandsüberführungsfunktion δ : S × Σ → S , der Ausgabefunktion λ : S → ∆ und den Startzustand s ∈ S 0 . WS 2006/2007 Computer Science Seite 186 93 Typ-2 Grammatiken: Kontextfreie Grammatiken Nichtreguläre Sprachen können durch Typ-3 Grammatiken nicht erzeugt werden. Hierzu benötigt man Kontextfreie Grammatiken Definition: Eine Grammatik G = ( Σ , N , P , S ) mit dem Terminalalphabet ∑ , dem davon disjunkten Nichtterminalalphabet N, der endlichen Produktionsmenge P und dem Startsymbol über ∑ , falls s∈N heißt kontextfrei P ⊆ N × (Σ ∪ N)∗ WS 2006/2007 Computer Science Seite 187 Eine Sprache L heißt kontextfrei über ∑ , falls es eine kontextfreie Grammatik G über ∑ mit L = L(G) gibt. Kontextfreie Grammatiken und Sprachen heißen auch Typ-2 Grammatiken bzw. Typ-2 Sprachen. Jede Typ-3 Grammatik ist kontextfrei. Die Klasse der regulären Sprachen über einem Alphabet ∑ ist eine Teilklasse der Klasse der kontextfreien Sprachen über ∑ . WS 2006/2007 Computer Science Seite 188 94 Kellerautomaten Typ-3 Grammatiken sind äquivalent zu endlichen Automaten. Nun suchen wir Automaten, die äquivalent zu kontextfreien Grammatiken sind. Dies sind die Kellerautomaten. Definition: Ein nichtdeterministischer Kellerautomat ist ein System mit dem Eingabe K = (Σ , S , Γ, δ , s0 , ⊥, F ) mit dem Eingabealphabet Σ, der endlichen Zustandsmenge S , dem Kelleralphabet Γ , der Zustandsüberführung ∗ δder : SZustandsüberführungsfu, × (Σ ∪ {ε }) × Γ → 2S ×Γ dem Startzustand s0 ∈ S , dem KellerBottomsymbol ⊥∈ Γ und der Endzustandsmenge F ⊆ S. . WS 2006/2007 Computer Science Seite 189 95