Link - auf ThomasKaelin.ch

Werbung
Programmieren 2
Programmieren 2 (Java)
Zusammenfassung v1.0
Kälin Thomas, Abteilung I
SS 06
Kälin Thomas, Abt I
1/35
06.07.2006
Programmieren 2
1.
2.
Rekursive Methoden........................................................................................................... 6
Iterativ / Rekursiv / Explizit .............................................................................................. 6
2.1.
Beispiel Reihen ............................................................................................................... 6
2.2.
Zusammenhang: Rekursion / Induktion ........................................................................ 6
3.
Designpatterns ................................................................................................................... 6
3.1.
Struktur eines Design Patterns ...................................................................................... 6
3.2.
Objekt Adapter / Klassenadapter................................................................................... 6
4.
Analyse von Algorithmen ................................................................................................... 6
4.1.
Laufzeitverhalten ........................................................................................................... 7
4.2.
Experimentelle Studien .................................................................................................. 7
4.3.
Theoretische Analyse...................................................................................................... 7
4.3.1. Wichtige Funktionen............................................................................................................ 7
4.3.2. Primitive Operationen .......................................................................................................... 7
4.3.3. Pseudocode / Zählen der primitiven Operationen .................................................................. 7
4.3.4. Asymptotische Algorithmus Analyse...................................................................................... 7
4.4.
Big-Oh / Big-Omega / Big-Theta.................................................................................... 7
4.4.1. Big-Oh Regeln..................................................................................................................... 8
4.4.2. Beispiele............................................................................................................................. 8
4.4.3. Benötigte Mathematik ......................................................................................................... 8
4.5.
Analyse log(n) ................................................................................................................ 8
5.
Rekursion ........................................................................................................................... 8
5.1.
Endrekursion .................................................................................................................. 8
5.2.
Binäre Rekursion ............................................................................................................ 8
6.
Stack................................................................................................................................... 8
6.1.
Operationen.................................................................................................................... 8
6.2.
Exceptions ...................................................................................................................... 8
6.3.
Arraybasierter Stack....................................................................................................... 8
7.
Queue ................................................................................................................................. 9
7.1.
Operationen.................................................................................................................... 9
7.2.
Exceptions ...................................................................................................................... 9
7.3.
Arraybasierte Queue (Ringspeicher) .............................................................................. 9
8.
Linked List .......................................................................................................................... 9
8.1.
Operationen.................................................................................................................... 9
8.2.
Anwendungen................................................................................................................. 9
9.
Array-List............................................................................................................................ 9
9.1.
Operationen.................................................................................................................... 9
9.2.
Exceptions ...................................................................................................................... 9
9.3.
Array-basierte Implementierung ................................................................................. 10
9.4.
Array wachsen lassen ................................................................................................... 10
10.
Node-List .......................................................................................................................... 10
10.1. Operationen.................................................................................................................. 10
10.2. AddAfter / Remove....................................................................................................... 10
11.
Sequence .......................................................................................................................... 10
11.1. Operationen.................................................................................................................. 10
11.2. Implementierung mit Linked List ................................................................................. 10
11.3. Arraybasierte Implementierung................................................................................... 11
12.
Iterator ............................................................................................................................. 11
12.1. Operationen.................................................................................................................. 11
12.2. For / Iterator ................................................................................................................ 11
13.
XML (eXtended Markup Language).................................................................................. 11
13.1. Beispiel ......................................................................................................................... 11
13.2. Attribute oder Child-Elemente?.................................................................................... 11
13.3. Namen .......................................................................................................................... 11
13.4. CDATA ........................................................................................................................... 11
13.5. Wohlgeformtes XML / Gültigkeit.................................................................................. 11
14.
Trees / Bäume .................................................................................................................. 12
Kälin Thomas, Abt I
2/35
06.07.2006
Programmieren 2
14.1. Baum Terminologie ...................................................................................................... 12
14.2. Operationen des Tree ADT............................................................................................ 12
14.3. Traversierungen ........................................................................................................... 12
14.3.1.
Preorder ....................................................................................................................... 12
14.3.2.
Postorder...................................................................................................................... 12
14.3.3.
Inorder (Binäre Bäume)................................................................................................. 12
14.3.4.
Euler Tour .................................................................................................................... 12
14.4. Binäre Bäume ............................................................................................................... 12
14.4.1.
Eigenschaften echter Binärbäume .................................................................................. 13
14.5. Implementierungen Binärer Bäume............................................................................. 13
14.5.1.
Linked List .................................................................................................................... 13
14.5.2.
Arraybasiert .................................................................................................................. 13
14.6. Template Method Pattern (Schablonenmuster)........................................................... 13
14.7. Beschreibung von Binär-Bäumen ................................................................................. 13
15.
Priority Queue ADT ........................................................................................................... 14
15.1. Operationen.................................................................................................................. 14
15.2. Implementierung mittels unsortierter Liste................................................................. 14
15.2.1.
Selection Sort ............................................................................................................... 14
15.3. Implementierung mittels sortierter Liste..................................................................... 14
15.3.1.
Insertion Sort................................................................................................................ 14
15.3.2.
In-Place Insertion Sort................................................................................................... 14
16.
Adaptierbare Priority Queue ............................................................................................ 14
16.1. Operationen.................................................................................................................. 14
16.2. Lokalisierung von Entries ............................................................................................. 14
17.
Heap ADT .......................................................................................................................... 14
17.1. Vollständiger Binärbaum .............................................................................................. 14
17.2. Implementierung einer Priority Queue mittels Heap................................................... 15
17.2.1.
Einfügen eines Elements................................................................................................ 15
17.2.2.
Upheap-Verfahren (Bubbeling)....................................................................................... 15
17.2.3.
Entfernen eines Elements .............................................................................................. 15
17.2.4.
Downheap-Verfahren .................................................................................................... 15
17.2.5.
Heap-Sort ..................................................................................................................... 15
17.3. Implementierung mittels Vector .................................................................................. 15
17.4. Konstruktion eines Heaps............................................................................................. 15
17.4.1.
Top-Down .................................................................................................................... 15
17.4.2.
Bottom-Up.................................................................................................................... 15
18.
Map ADT ........................................................................................................................... 16
18.1. Operationen.................................................................................................................. 16
18.2. Implementierung mittels Linked-List........................................................................... 16
18.3. Sentinel-Trick ............................................................................................................... 16
19.
Dictionary ADT.................................................................................................................. 16
19.1. Operationen.................................................................................................................. 16
19.2. Implementierung mittels Linked-List........................................................................... 16
19.3. Implementierung mittels Map...................................................................................... 16
19.4. Binary Search ............................................................................................................... 16
20.
Hash-Tabellen .................................................................................................................. 17
20.1. Hash-Funktionen .......................................................................................................... 17
20.1.1.
Hash-Funktion .............................................................................................................. 17
20.1.2.
Kompressions-Funktion.................................................................................................. 17
20.2. Kollisionsbehandlung ................................................................................................... 17
20.2.1.
Geschlossene Adressierung (offenes Hashverfahren) ....................................................... 17
20.2.2.
Offene Adressierung (geschlossene Hashverfahren) ........................................................ 17
20.2.3.
Doppeltes Hashing (Offene Adressierung)....................................................................... 17
20.3. Performance ................................................................................................................. 17
21.
Skip-List............................................................................................................................ 18
21.1. Definition einer Skip List .............................................................................................. 18
21.1.1.
Perfekte Skip List .......................................................................................................... 18
Kälin Thomas, Abt I
3/35
06.07.2006
Programmieren 2
21.1.2.
Random Skip List .......................................................................................................... 18
21.2. Java-Code ..................................................................................................................... 18
21.2.1.
SkipListNode ................................................................................................................. 18
21.2.2.
SkipList ........................................................................................................................ 18
21.2.3.
Suchen ......................................................................................................................... 18
21.2.4.
Einfügen....................................................................................................................... 19
21.2.5.
Löschen........................................................................................................................ 19
21.2.6.
Zufallsgenerator............................................................................................................ 19
21.3. Implementierung mittels Quad-Nodes......................................................................... 19
21.4. Analysen ....................................................................................................................... 19
21.4.1.
Speicherplatz ................................................................................................................ 19
21.4.2.
Höhe ............................................................................................................................ 19
21.4.3.
Laufzeiten..................................................................................................................... 19
22.
Binäre Suchbäume ........................................................................................................... 20
22.1. Höhe des Baums ........................................................................................................... 20
22.2. Implementierung.......................................................................................................... 20
22.3. Suche ............................................................................................................................ 20
22.4. Einfügen........................................................................................................................ 20
22.5. Löschen......................................................................................................................... 20
22.5.1.
Symetrischer Nachfolger................................................................................................ 20
22.6. Inorder-Methode .......................................................................................................... 20
23.
AVL Baum ......................................................................................................................... 21
23.1. Balance ......................................................................................................................... 21
23.2. Einfügen........................................................................................................................ 21
23.2.1.
Cut/Link Restrukturierungs-Algorithmus.......................................................................... 21
23.3. Löschen......................................................................................................................... 21
23.4. Laufzeiten ..................................................................................................................... 21
24.
Merge Sort ........................................................................................................................ 22
24.1. Code.............................................................................................................................. 22
25.
Quick-Sort ........................................................................................................................ 22
25.1. Laufzeit ......................................................................................................................... 22
25.2. Beispiele ....................................................................................................................... 22
26.
Bucket-Sort ...................................................................................................................... 23
27.
Radix-Sort ........................................................................................................................ 23
27.1. Beispiel: Sortierung von 3-Bit Integers........................................................................ 23
28.
Untere Grenze der Sortierung .......................................................................................... 23
29.
Sets................................................................................................................................... 23
29.1. Grundoperationen ........................................................................................................ 23
29.1.1.
Vereinigung (Union) ...................................................................................................... 23
29.1.2.
Durchschnitt (Intersection) ............................................................................................ 23
29.1.3.
Differenz (Subtraction) .................................................................................................. 23
29.2. Operationen.................................................................................................................. 24
29.3. Set-Varianten in Java ................................................................................................... 24
30.
Automaten ........................................................................................................................ 24
30.1. Akzeptor ....................................................................................................................... 24
30.2. Transduktor .................................................................................................................. 24
30.2.1.
Mealy / Moore – Automaten........................................................................................... 24
31.
Pattern Matching.............................................................................................................. 24
31.1. Allgemeines .................................................................................................................. 24
31.2. Brute-Force................................................................................................................... 25
31.2.1.
Worst-Case Beispiel....................................................................................................... 25
31.3. Boyer-Moore ................................................................................................................. 25
31.3.1.
Last-Occurrence Funktion .............................................................................................. 25
31.3.2.
Berechnung der Verschiebung ....................................................................................... 25
31.3.3.
Analyse ........................................................................................................................ 26
31.3.4.
Zusammenfassung ........................................................................................................ 26
31.4. KMP-Algorithmus.......................................................................................................... 26
Kälin Thomas, Abt I
4/35
06.07.2006
Programmieren 2
31.4.1.
KMP-Fehlfunktion .......................................................................................................... 26
31.4.2.
Beispiel......................................................................................................................... 26
31.4.3.
Analyse ........................................................................................................................ 26
31.4.4.
Zusammenfassung ........................................................................................................ 26
32.
REGEX............................................................................................................................... 26
32.1. Metazeichen.................................................................................................................. 26
32.2. Tabellen ........................................................................................................................ 27
32.3. Quantoren..................................................................................................................... 27
32.3.1.
Beispiele....................................................................................................................... 27
32.4. Java Beispiel ................................................................................................................. 27
33.
Graphen ............................................................................................................................ 27
33.1. Begriffe ......................................................................................................................... 27
33.2. Operationen.................................................................................................................. 28
33.3. Kanten-Listen Struktur................................................................................................. 28
33.4. Adjadenz-Listen Struktur ............................................................................................. 28
33.5. Adjazenz-Matrix Struktur ............................................................................................. 29
33.6. Performance ................................................................................................................. 29
33.7. Adjazenz-Matrix ........................................................................................................... 29
34.
Tiefensuche (Depth-First Search: DFS)............................................................................ 29
34.1. DFS-Algorithmus .......................................................................................................... 29
34.2. Spezialisierung ............................................................................................................. 30
34.2.1.
Pfade finden ................................................................................................................. 30
34.2.2.
Zyklen finden ................................................................................................................ 30
34.3. Gerichtete Tiefensuche................................................................................................. 30
35.
Breitensuche (Breadth-First Search: BFS) ....................................................................... 30
35.1. BFS-Algorithmus........................................................................................................... 30
35.2. Applikationen ............................................................................................................... 31
36.
Gerichteter Graph / Digraph ............................................................................................ 31
36.1. Transitiver Abschluss.................................................................................................... 31
36.2. Floyd-Warshall’s Algorithmus ...................................................................................... 31
36.2.1.
Beispiel......................................................................................................................... 31
36.3. Topologische Sortierung............................................................................................... 31
37.
Shortest Path / Kürzester Pfad ........................................................................................ 32
37.1. Dijkstra’s Algorithmus .................................................................................................. 32
37.1.1.
Beispiel......................................................................................................................... 32
37.1.2.
Probleme ...................................................................................................................... 32
37.2. Bellman-Ford Algorithmus............................................................................................ 32
37.3. DAG-basierter Algorithmus .......................................................................................... 33
38.
Minimum Spanning Tree .................................................................................................. 33
38.1. Kruskal’s Algorithmus................................................................................................... 33
38.2. Prim-Jarnik’s Algorithmus ............................................................................................ 34
38.3. Baruvka’s Algorithmus ................................................................................................. 34
39.
XML: Document Type Definition (DTD) ............................................................................ 34
39.1. Beispiel (interne DTD) .................................................................................................. 34
39.2. Beispiel (externe DTD) ................................................................................................. 35
39.3. Spezielles...................................................................................................................... 35
Kälin Thomas, Abt I
5/35
06.07.2006
Programmieren 2
1. Rekursive Methoden
Werte der aktuellen Parameter, für die kein rekursiver Aufruf ausgeführt wird, werden als „base
cases“ oder „Verankerung“ bezeichnet. Jede mögliche rekursive Aufrufkette muss einen base case
erreichen. Jeder rekursive Aufruf sollte so definiert sein, dass er die Ausführung in Richtung
Verankerung bewegt.
Beispiel: Fakultät
public static int factor(int n) {
if (n == 0) { return 1; //Verankerung }
else { return n * factor(n-1); }
}
2. Iterativ / Rekursiv / Explizit
Iterativ
lin(n)=sum(i=1;n;i)
sqr(n)=sum(i=1;n;i^2)
Rekursiv
lin(1)=1
lin(n)=n+lin(n-1)
sqr(1)=1
sqr(n)=n^2+sum(n-1)
Explizit
lin(n)=n*(n+1) / 2
sqr(n)=n*(n+1)*(2n+1)/6
2.1. Beispiel Reihen
Reihe: 1,
Iterativ:
Rekursiv:
Explizit:
5,
an
a1
an
9, 13, 17, …
= 1 + sum(i=1; n-1; 4)
= 1; an = 4 + an-1;
= 4n-3
Reihe: 1,
Iterativ:
Rekursiv:
Explizit:
6,
sn
s1
sn
15, 28, … (Summe von Reihe 1!)
= sum(i=1; n; 4n-3);
= 1; sn = 4n-3+sn-1;
= n*(a1+an)/2 = n*(1+4n-3)/2;
//Explizite Formel verw.
//Explizite Formel verw.
//Explizite Formel verw.
2.2. Zusammenhang: Rekursion / Induktion
Die Verankerung entspricht der Abbruchbedingung in der Rekursion und der Induktionsschritt
entspricht einem rekursiven Aufruf. Man beweist n+1 durch n und den Induktionsschritt. Dies ist in
der Rekursion ebenfalls gegeben durch die Berechnung von n+1 aus n.
3. Designpatterns
Ein Designpattern ist ein Lösungsansatz eines „typischen“ Software Designproblems, welches in sehr
unterschiedlichen Situationen verwendet werden kann.
3.1. Struktur eines Design Patterns
ƒ Mustername: Benennung des Patterns
ƒ Problemabschnitt: Beschreibung, wann das Pattern anzuwenden ist
ƒ Lösungsabschnitt: Beschreibung der Elemente, aus denen das Pattern besteht.
Implementierung, nur ein generelles Konzept.
ƒ Konsequenzenabschnitt: Liste der Vor- und Nachteile bei der Anwendung des Patterns
Keine
3.2. Objekt Adapter / Klassenadapter
4. Analyse von Algorithmen
Ein Algorithmus ist ein Schritt-für-Schritt Vorgehen zum Lösen eines Problems mit endlichem
Zeitaufwand. Kann gut mit einem Kochrezept verglichen weden.
Kälin Thomas, Abt I
6/35
06.07.2006
Programmieren 2
4.1. Laufzeitverhalten
Die meisten Algorithmen transferieren Eingaben in Ausgaben. Die Laufzeit eines Algorithmus nimmt
typischerweise mit dem Umfang der Eingabe zu. Wir konzentrieren uns bei der Analyse des
Laufzeitverhaltens auf den „Worst Case“, da dies vergleichsweise einfach zu analysieren ist.
4.2. Experimentelle Studien
Wir schreiben ein Programm, welches den Algorithmus implementiert und lassen dieses mit
unterschiedlichen, repräsentativen Eingaben laufen. Dabei zeichnen wir die Laufzeiten beispielsweise
mit System.currentTimeMillis() auf und vergleichen die Resultate. Problem: Der Algorithmus muss
vollständig Implementiert werden (Zeitaufwand!), die Auswahl der Eingaben ist sehr kritisch und kann
zu falschen Schlüssen führen. Natürlich muss immer auf derselben HW und SW getestet werden.
4.3. Theoretische Analyse
Hierbei verwendet man einen „high-level“ Beschreibung des Algorithmus (Pseudocode), keine
Implementierung. Man sucht eine funktionale Beschreibung (als Funktion der Eingabe). Hierdurch
werden alle möglichen Eingaben berücksichtig, ausserdem ist die Methode unabhängig von HW und
SW.
4.3.1.
Wichtige Funktionen
ƒ konstant < log < linear < n-log < quadratisch < kubisch < exponentiell
ƒ 1 < log(n) < n < n*log(n) < n2 < n3 < 2n
4.3.2.
Primitive Operationen
Dies sind Basis-Operationen, die von einem Algorithmus ausgeführt werden. Sie sind im Pseudocode
identifizierbar und unabhängig von der Programmiersprache. Eine Exakte Definition ist eher unwichtig.
im RAM (Random Access Machine) Modell benötigen Sie eine typische, konstante Zeit.
4.3.3.
Pseudocode / Zählen der primitiven Operationen
Algorithm arrayMax(A,n)
Input array A of n integers
Output maximum element of A
currentMax <- A[0]
2 //Zugriff=1, Zuweisung=1
for i<-1 to n-1 do {
1+2n //Zuweisung=1,Vergleichen=n,++=n
if (A[i] > currentMax) then {
2(n-1)
currentMax <- A[i] }
2(n-1) //Im dümmsten Fall!
increment counter i;
2(n-1) //i = i+1!!
}
return currentMax
1
-> 2 + 1+2n + 2(n-1) + 2(n-1) + 2(n-1) + 1 = 8n-2
4.3.4.
Asymptotische Algorithmus Analyse
Darunter versteht man das bestimmen des Laufzeitverhaltens in der big-Oh Notation. Dazu suchen wir
zuerst das worst-case Verhalten als Funktion primitiver Operationen (8n-2). Diese Funktion
beschreiben wir in der big-Oh Notation (arrayMay läuft in O(n) Zeit).
4.4. Big-Oh / Big-Omega / Big-Theta
f(n) ist O(g(n)) falls ein c > 0 und ein no >= 1 existiert, so dass:
f(n) <= c*g(n) für n>=n0
f(n) ist Ω(g(n)) falls ein c > 0 und ein no >= 1 existiert, so dass:
f(n) >= c*g(n) für n>=n0
f(n) ist Θ(g(n)) falls ein c’>0 und c’’0 und ein no >= 1 existiert, so dass:
c’*g(n) <= f(n) <= c’’*g(n) n>=n0
ƒ Die big-Oh Notation gestattet die Angabe einer oberen Grenze für die Wachstumsfunktion. Die
Aussage „f(n) is O(g(n))“ bedeutet: „Die Wachstumsfunktion f(n) wird oben begrenzt durch g(n)“.
ƒ f(n) ist Ω(g(n)) falls f(n) asymptotisch grösser oder gleich g(n) ist (unten begrenzt durch).
ƒ f(n) ist Θ(g(n)) falls f(n) asymptotisch gleich wie g(n) ist (oben und unten begrenzt).
Kälin Thomas, Abt I
7/35
06.07.2006
Programmieren 2
4.4.1.
Big-Oh Regeln
ƒ Lassen Sie alle tieferen Potenzen weg
ƒ Lassen Sie alle Konstanten weg
ƒ Lassen Sie den Koeffizienten der höchsten Potenz weg
4.4.2.
Beispiele
7n-2 ist O(n)
3n3+20n2+5 ist O(n3)
3*log(n) + 5 ist O(log(n))
5n2 ist Ω(n2)
5n2 ist Θ(n2)
4.4.3.
logb(x*y)
logb(x/y)
logb(x^a)
logb(a)
>
>
>
>
>
c=7, no=1
c=4, no=21
c=8, no=2
c=5, no=1
c’=5,c’’=5, no=1
Benötigte Mathematik
=
=
=
=
logb(x) + logb(y)
logb(x) – logb(y)
a * logb(x)
logx(a) / logx(b)
a(b+c)
a(b-c)
abc
b
bc
=
=
=
=
=
ab * ac
ab / ac
(ab)c
aloga(b)
a*c*loga(b)
4.5. Analyse log(n)
In einem Intervall der Länge n werden n Werte abgespeichert. Das Intervall wird halbiert. Es stehen
jetzt nur noch n/2 Werte zur Verfügung. Jetzt wird ein Teilintervall nochmals halbiert, somit stehen
nur noch n/4 Werte zur Verfügung. Abbruch, wenn nur noch ein Wert vorhanden ist. Dieses Verhalten
ist typisch für Logarithmen mit O(log(n))!
5. Rekursion
ƒ Überprüfung der Verankerung (Base Cases). Jede rekursive Aufrufkette muss schliesslich zu einer
Verankerung führen.
ƒ Jeder rekursive Aufruf wird typischerweise in Richtung „base case“ führen.
ƒ Beim Programmieren rekursiver Methoden ist es wichtig, die Methoden so zu definieren, dass die
Rekursion einfach wird.
5.1. Endrekursion
Endrekursion tritt dann auf, wenn eine linear rekursive Methode als letzten Schritt den rekursiven
Aufruf ausführt.
5.2. Binäre Rekursion
Binäre Rekursion tritt immer dann auf, wenn zwei rekursive Aufrufe in allen nicht terminalen Aufrufen
ausgeführt werden (Bsp: Fibonacci). Diese Algorithmen haben typischerweise ein Verhalten von O(2n)!
6. Stack
Ein Stack speichert beliebige Objekte. Einfügen und Löschen erfolgt gemäss dem LIFO-Schema.
Verwendung bei: History im Webbrowser, Undo-Funktion, Methodenaufrufe in der JVM
6.1. Operationen
push(Object): Ein Element einfügen
Object pop(): Entfernen und Zurückgeben des obersten Elementes.
Object top(): Liefert das oberste Element, ohne dieses zu entfernen.
int size(): Liefert die Anzahl gespeicherter Elemente
bool isEmpty(): Zeigt an, ob Elemente im Stack vorhanden sind.
6.2. Exceptions
ƒ Bei einem leeren Stack wird EmptyStackException geworfen.
6.3. Arraybasierter Stack
Elemente werden von links nach rechts in das Array eingefügt. In einer Variablen wird der Index des
obersten Elements abgespeichert. Für den Fall, dass das Array voll ist, muss eine „FullStackException“
programmiert werden (Implementierungsabhängige Exception!). Jede Operation benötigt O(1) Zeit.
Kälin Thomas, Abt I
8/35
06.07.2006
Programmieren 2
7. Queue
Einfügen und Löschen entspricht dem FIFO-Prinzip. Das Einfügen erfolgt am Ende, das Entfernen am
Anfang. In Java ist nur ein Interface für „Queue“ vorhanden. Verwendung bei: Wartelisten, Zugriff auf
gemeinsame Ressourcen, Multiprogramming
7.1. Operationen
enqueue(Object): Einfügen eines Elementes am Ende der Queue
Object dequeue(): Entfernen & zurückgeben d. Elements am Anfang der Queue
Object front(): Liefert das erste Element, ohne es zu entfernen
int size(): Liefert die Anzahl gespeicherter Elemente.
bool isEmpty(): Zeigt an, ob noch Elemente gespeichert sind
7.2. Exceptions
ƒ Entfernen eines Elements aus einer leeren Queue führt zu einer EmptyQueueException.
7.3. Arraybasierte Queue (Ringspeicher)
Wir nutzen ein Array auf zirkuläre Art und Weise. Zwei Variablen merken sich den Anfang und das
Ende der Queue.
ƒ int size(): (n-f+r) % n => (10-3+5) % 10 = 2 Elemente vorhanden
ƒ bool isEmpty(): (f==r)
8. Linked List
Eine einfach verkettete Liste besteht aus einer Sequenz von Knoten (Nodes). Jeder Knoten besitzt ein
Element (Inhalt) und einen Link zum nächsten Knoten. Der letzte Knoten zeigt auf null. Alternative:
Spezielle Header und Trailer-Elemente definieren, analog zur Node-List.
8.1. Operationen
addFirst(Object): Element am Anfang der Liste einfügen
addLast(Object): Element am Schluss der Liste einfügen
Object removeFirst(): Element am Anfang der Liste auslesen
Object removeLast(): Element am Ende der Liste auslesen
8.2. Anwendungen
ƒ Stack: Mit Hilfe einer einfach verketteten Liste kann ein Stack implementiert werden. Das top()Element entspricht hierbei dem ersten Knoten der Liste. Speicherverbrauch geht mit O(n), jede
Stackoperation benötigt O(1) Zeit.
ƒ Queue: Kann auch mittels einer einfach verketten Liste aufgebaut werden. Das erste Element der
Queue (Kopf) entspricht dem ersten Knoten der Liste. Das Endelement der Queue entspricht dem
letzten Knoten der Liste. Speicherbedarf ist O(n), jede Operation benötigt O(1) Zeit.
9. Array-List
Der Array-List ADT erweitert den Arraybegriff indem beliebige Objekte als Elemente abgespeichert
werden dürfen. Ein Element kann eingefügt, gelöscht oder zugegriffen werden, indem sein Index
angegeben wird.
9.1. Operationen
Element get(int i): Liefert das Element i, ohne es zu entfernen. (O(1)).
Element set(int i, Element e): Ersetzt das Element an der Stelle i durch e
und gibt das alte zurück. (O(1)).
add(int i, Element e): Fügt ein neues Element an der Stelle i ein, ohne das
bestehende zu überschreiben. (O(n) wegen Suche).
Element remove(int i): Entferne i und liefere das Element zurück. (O(n))
int size(): Grösse der Array-List (O(1))
boolean isEmpty(): Container leer? (O(1))
9.2. Exceptions
Bei Zugriff ausserhalb des erlaubten Bereichs wird eine Exception geworfen.
Kälin Thomas, Abt I
9/35
06.07.2006
Programmieren 2
9.3. Array-basierte Implementierung
ƒ Wir verwenden ein Array V der Grösse N. Eine Variable n speichert die Grösse des Vektors.
Operation get(i) kann als O(1) Zeit-Operation implementiert werden: V[i].
ƒ Bei add(i,e) müssen wir zuerst Platz schaffen und die Elemente ab Position i nach hinten
verschieben. Im schlimmsten Fall (i=0) benötigen wir O(n) Zeit.
ƒ In der Operation remove(i) müssen wir das entstehende Loch auffüllen, und die Elemente nach
vorne verschieben. Wir benötigen wieder O(n) Zeit im schlimmsten Fall.
ƒ Bei zirkulärer Nutzung benötigen Einfügen und Löschen am Ende / Beginn des Arrays nur O(1) Zeit.
9.4. Array wachsen lassen
Wir können das Werfen einer Exception beim Einfügen eliminieren, indem wir das Array wachsen
lassen. Dazu gibt es zwei Strategien:
ƒ Inkrementelle Strategie: Wir erhöhen die Arraygrösse um eine Konstante c. -> O(n)
ƒ Verdoppelungsstrategie: Wir verdoppeln jedes Mal das Array. -> O(1)
10.
Node-List
Die Node-List ist stark verwandt mit der Linked-List. Der Unterschied ist, dass die Node-List doppelt
verkettet ist, also jeder Node Referenzen auf das vorherige (prev) und nachfolgende (next) Element
besitzt.
10.1. Operationen
int size(), bool isEmpty(): Generische Methoden
Node first(), Node last(): Zugriff auf erster/letzter Node
setPrev(Node), setNext(Node): Vorheriger/Nächster Node setzen
Node getPrev(), Node getNext(): Vorheriger/Nächster Node abrufen
setElement(e): Inhalt vom aktuellen Node mit Element e füllen
e getElement(): Inhalt vom aktuellen Node abrufen
addBefore(Node,e), addAfter(Node,e): Element e vor/nach Node einfügen.
addFirst(e), addLast(e): Element am Anfang/Ende der Node-List einfügen.
remove(Node): Element aus der Node-List entfernen
10.2. AddAfter / Remove
Node addAfter(p,e) {
Node v = new Node();
v.setElement(e);
v.setPrev(p);
v.setNext(p.getNext());
p.getNext().setPrev(v);
p.setNext(v);
return v; }
11.
Object remove(p) {
Object tmp = p.getElement();
p.getPrev().setNext(p.getNext());
p.getNext().setPrev(p.getPrev());
p.setPrev(null);
p.setNext(null);
return tmp; }
Sequence
Fasst die Array- und Node-List zusammen. Zugriff möglich über Index oder Position. Generischer
Ersatz für Stack, Queue, Array- oder Node-List.
11.1. Operationen
int size(), bool isEmpty(): Generische Methoden
//Operationen der Array-List (e get(i), set(i,e), …)
//Operationen der Node-List (Node first(), Node last(), Node getPrev(), …)
//Bridge-Operationen: Node atIndex(i), i indexOf(Node)
11.2. Implementierung mit Linked List
ƒ
ƒ
ƒ
ƒ
Grafik der Implementierung siehe „Node-List“
Knoten implementieren Position und speichern Element, Link auf Vorgänger / Nachfolger.
Es existieren je ein spezieller Header und Trailer-Knoten.
Index-basierte Zugriffe suchen (abzählen der Schritte) immer vom Anfang / Ende der Liste und sind
somit linear in der Zeit (O(n)).
Kälin Thomas, Abt I
10/35
06.07.2006
Programmieren 2
11.3. Arraybasierte Implementierung
ƒ Jeder Arrayeintrag enthält eine Referenz auf eine Position. Diese enthalten den Index und eine
Referenz auf das Element.
ƒ Variablen F und L speichern die erste, bzw. letzte belegte Position.
12.
Iterator
Ein Iterator abstrahiert den Prozess des Scannens über eine Sammlung von Elementen.
Typischerweise wird ein Iterator zusammen mit einer anderen ADT verwendet. Dabei wird das
betreffende ADT durch eine Methode „Iterator iterator()“ ergänzt.
12.1. Operationen
bool hasNext(): Noch weitere Elemente vorhanden?
e next(): Lieft das nächste Element
12.2. For / Iterator
For wird bei Arrays verwendet, ein Iterator für komplexe Datenstrukturen (Liste). Nachfolgend noch
ein Beispiel einer „For-Each“-Schleife:
String arrValues[] = {„Das“, „ist“, „Mist.“};
for (String strValue : arrValues) {
//strValue enthält der Reihe nach alle Elemente von arrValues.
}
13.
XML (eXtended Markup Language)
13.1. Beispiel
<?xml version=“1.0“ encoding“ASCII“ standalone=“no“ ?>
<!-- Kommentar: XML Dokument muss immer mit obiger Zeile beginnen! -->
<root>
<example>Das ist ein Beispiel</example>
<entitiy>" - ' - < - > - &</entitity>
<!-- Obige Zeile gib folgendes aus: " – ' - < - > - & -->
<attribute value=“Test“>Attribute</attribute>
<spezialfall value=“LeererTag“ />
</root>
13.2. Attribute oder Child-Elemente?
Es gibt keine grundsätzliche Regel, wann Attribute und wann Child-Elemente verwendet werden.
Faustregel: Daten in Elemente, Meta-Daten (ID, URL) in Attribute
13.3. Namen
Erlaubt sind: Buchstaben, Zahlen, _-.:. Ausserdem muss Buchstabe / Unterstrich am Anfang sein!
13.4. CDATA
Spezielle Kennzeichnung für komplexe Teile. Es können alle möglichen Zeichen verwendet werden!
<![CDATA[ for (i=0; i<2; i++) {} ]]>
13.5. Wohlgeformtes XML / Gültigkeit
Ein Dokument ist dann wohlgeformt, wenn es der XML-Spezifikation entspricht. Gültigkeit entspricht
der DTD (Document Type Definition), kann mit XML-Parser überprüft werden.
ƒ Das Dokument besitzt nur ein Root Element (Tag auf der obersten Ebene).
ƒ Zu allen öffnenden Tags existieren schliessende Tags.
ƒ Die Elemente sind korrekt verschachtelt (schematisch: <a><b>Text</b></a>).
ƒ Gross-/ Kleinschreibung wird beachtet (Nicht erlaubt: <Anfang>…</anfang>)
ƒ Attributwerte stehen zwischen Anführungszeichen (<a b="Wert“/> ist erlaubt, <a b=Wert/> nicht).
ƒ Entities müssen vor ihrem Einsatz deklariert werden (Entities: Texte, welche mehrfach verwendet,
einmal definiert werden).
Kälin Thomas, Abt I
11/35
06.07.2006
Programmieren 2
14.
Trees / Bäume
In der Informatik repräsentieren Bäume abstrakte, hierarchische Datenstrukturen.
14.1. Baum Terminologie
ƒ
ƒ
ƒ
ƒ
ƒ
ƒ
ƒ
ƒ
ƒ
Wurzel: Knoten ohne Elternknoten (A)
Interner Knoten: Knoten mit mindestens einem Kind (A, B, F, C)
Externer Knoten: Auch Blatt genannt. Knoten ohne Kind. (E, I, J, K, G, H, D)
Vorgänger: Knoten einer höheren Ebene (B ist Vorgänger von E)
Nachfolger. Knoten einer tieferen Ebene (E ist Nachfolger / Kind von B)
Tiefe eines Knotens: Anzahl Vorgänger (Tiefe A:0, B:1, E:2, K:3)
Höhe eines Knotens: Maximale Anzahl Nachfolger bis zu einem Blatt hinunter (Höhe B: 2)
Höhe des Baums: Höhe der Wurzel (Höhe A: 3)
Subtree: Baum aus einem Knoten und seinen Nachfolgern
14.2. Operationen des Tree ADT
int size(): Anzahl Knoten des Baums
bool isEmpty(): Knoten enthalten?
Iterator elements(): Iterator über die Elemente (Inhalte)
Iterator positions(): Iterator über die Knoten
Knoten root(): Gibt den Wurzel-Knoten zurück
Knoten parent(Knoten): Gibt den Eltern-Knoten eines Knotens zurück
Iterator children(Knoten): Iterator über alle Nachfolger eines Knotens
bool isInternal(Knoten): Ist dieser Knoten Intern?
bool isExternal(Knoten): Ist dieser Knoten ein Blatt?
bool isRoot(Knoten): Ist dieser Knoten die Wurzel?
E replace(Knoten,E): Ersetze Element eines Knotens. Gibt altes E zurück!
14.3. Traversierungen
14.3.1.
Preorder
Bei der Preorder Traversierung wird ein Knoten VOR seinen Nachfolgern besucht. Anwedung: Drucken
eines strukturierten Dokuments.
ƒ Beispiel: A, B, E, F, I, J, K, C, G, H, D (Kursiv: Blätter)
14.3.2.
Postorder
In einer Postorder Traversierung wird ein Knoten NACH seinen Nachfolgern besucht. Anwendung:
Angabe des verbrauchten Speichers in einem Verzeichnis.
ƒ Beispiel: E, I, J, K, F, B, G, H, C, D, A (Kursiv: Blätter)
14.3.3.
Inorder (Binäre Bäume)
In einer Inorder Traversierung wird ein Knoten NACH seinem linken Subtree und VOR seinem rechten
Subtree besucht. Funktioniert NUR bei binären Bäumen. Anwendung: Darstellung von binären
Bäumen.
ƒ Beispiel OHNE Blatt J und D: E, B, I, F, K, A, G, C, H („Von links nach rechts gelesen“).
14.3.4.
Euler Tour
Generische Traversierung von BINÄREN Bäumen. Jeder Knoten wird 3 mal besucht: Einmal von links
(preorder), einmal von unten (inorder), einmal von rechts (postorder).
ƒ Beispiel OHNE Blatt J und D: LA, LB, LE, UE, UB, LF, LI, UF, LK, UK, RK, RF, RB, UA, LC, LG, UG, …
14.4. Binäre Bäume
Ein binärer Baum ist ein Baum mit folgenden Eigenschaften:
ƒ Jeder Knoten besitzt höchstens zwei Nachfolger
o Mit EXAKT zwei Nachfolgern spricht man von einem ECHTEN Binärbaum
ƒ Die Kinder eines Knotens sind ein geordnetes Paar (linkes Kind / rechtes Kind)
ƒ Anwendung:
o Arithmetische Ausdrücke (interne Knoten sind Operatoren, externe Knoten die Operanden)
o Entscheidungsprozesse (interne Knoten sind Fragen, externe Knoten die Entscheide „Ja“/“Nein“)
Kälin Thomas, Abt I
12/35
06.07.2006
Programmieren 2
14.4.1.
n
e
i
h
=
=
=
=
Eigenschaften echter Binärbäume
Knoten
Externe Knoten
Interne Knoten
Höhe
e
n
h
h
e <= 2h
h >= log2(e)
h >= log2(n+1)-1
= i+1
= 2e-1
<= i
<= (n-1)/2
14.5. Implementierungen Binärer Bäume
14.5.1.
Linked List
Jeder Knoten enthält einen Verweis auf das Element (Position ADT), den Elternknoten und die
beiden Kindknoten.
ƒ Aus diesem Knoten kann einfach ein allgemeines Speicherverfahren für normale Bäume
abgeleitet werden, indem die beiden Verweise auf die Kindkonten durch einen Verweis auf eine
Sequenz von Kindern ersetzt wird.
14.5.2.
Arraybasiert
Die Knoten werden in einem Array gespeichert. Index 0 wird dabei leer gelassen!
ƒ Root erhält Index 1.
ƒ Linkes Kind = 2 * Index des Eltern Elements
ƒ Rechtes Kind = 2 * Index des Eltern Elements + 1
14.6. Template Method Pattern (Schablonenmuster)
ƒ Absicht: Definieren eines Rumpfes (Skeletts) eines Algorithmus, wobei einige Schritte erst in
Subklassen spezifiziert werden. Das Template Method Muster lässt Subklassen Teile des
Algorithmus verfeinern, ohne die Struktur des Algorithmus zu verändern.
ƒ Problem: Zwei unterschiedliche Komponenten besitzen eine grosse Ähnlichkeit, haben aber kein
Interface und keine Implementierung gemeinsam. Falls eine Änderung nötig wird, müsste der
Aufwand mehrfach gemacht werden.
ƒ Lösung: Der Komponentendesigner muss entscheiden, welche Teile des Algorithmus
unveränderlich sind und welche angepasst werden können. Die gemeinsamen Teile werden in einer
abstrakten Klasse implementiert, die variablen Teile in einer Default-Implementierung oder gar nicht
festgehalten.
14.7. Beschreibung von Binär-Bäumen
Nur ein Knotentyp. Nachteil: Innere und
äussere Knoten können nur mittels „links“ /
„rechts“ Abfragen unterschieden werden.
Zwei Knotentypen. Nachteil: Baum wird nicht
optimal modelliert.
Safe Composite Pattern: Ein Knoteninterface.
Zwei implementierende Knoten (Blatt und innere
Knoten).
Gestattet
Überprüfung
zur
Kompilizierzeit.
Transparent
Composite
Pattern:
Ein
abstrakter Basisknoten. Zwei implementierende
Knoten (Blatt und innere Knoten). Muss mit
Exceptions arbeiten.
Kälin Thomas, Abt I
13/35
06.07.2006
Programmieren 2
15.
Priority Queue ADT
Eine Priority Queue speichert eine Collection von Entries. Jede Entry besteht aus einem Schlüssel-Wert
Paar, wobei derselbe Schlüssel mehrmals vorkommen darf. Der Schlüssel kann ein beliebiges Objekt
sein, auf welchem eine Ordnungsrelation definiert ist.
15.1. Operationen
void insert(k,v): Neuen Entry mit Schlüssel k und Wert v hinzufügen
E removeMin(): Liefert & entfernt die Entry mit kleinstem Schlüssel
E min(): Liefert Entry mit kleinstem Schlüssel ohne diese zu entfernen
int size(): Anzahl Elemente der PQ
bool isEmpty(): Elemente in der PQ vorhanden?
15.2. Implementierung mittels unsortierter Liste
Einfügen benötigt O(1) Zeit, da am Ende eingefügt wird. Abfragen benötigt O(n) Zeit, da die ganze
Sequenz durchlaufen werden muss, um das Minimum zu finden.
15.2.1.
Selection Sort
Wir wollen die Elemente einer unsortierten Liste sortiert ausgeben. Dabei müssen wir bei jeder
removeMin()-Operation den kleinsten Schlüssel suchen. Deshalb ist die Laufzeit O(n2)!
15.3. Implementierung mittels sortierter Liste
Einfügen benötigt O(n) Zeit, da die Einfügestelle gesucht werden muss. Abfragen benötigten dafür
O(1) Zeit, da der kleinste Schlüssel immer am Anfang der Liste ist.
15.3.1.
Insertion Sort
Wir wollen Elemente in eine sortierte Liste einfügen. Bei jedem insert()-Aufruf muss die komplette PQ
durchsucht werden, um die entsprechende Position zu finden. Laufzeit ist deshalb auch hier O(n2)!
15.3.2.
In-Place Insertion Sort
Hierbei werden zuerst alle Elemente in die PQ unsortiert eingefügt und erst anschliessend sortiert,
indem die Elemente direkt in der PQ vertauscht werden, bis die Ordnungsrelation stimmt. Auch O(n2)!
16.
Adaptierbare Priority Queue
Die adaptierbare PQ erweitert die normale PQ um die Fähigkeit, spezifische auf Entries der PQ
zuzugreifen. Bei der klassischen PQ ist nur Zugriff auf kleinsten Entry (min()) möglich.
16.1. Operationen
E remove(E): Entferne und liefere Entry E
k replaceKey(E,k): Ersetzt Key einer Entry. Gibt den alten Key zurück.
v replaceValue(E,v): Ersetzt Value einer Entry. Gibt alten Value zurück.
16.2. Lokalisierung von Entries
Um aufs spezifische Entries zugreifen zu können, muss jedes Mal die gesamte Datenstruktur
abgesucht werden.
17.
Heap ADT
Ein Heap ist ein Binärbaum, der in seinen Knoten Schlüssel speichert. Ausserdem muss die Bedingung
erfüllt sein, dass der Schlüssel eines Kindes immer grösser ist, als der Schlüssel seines Elternknotens.
17.1. Vollständiger Binärbaum
ƒ Ein Heap ist ein vollständiger Binärbaum. Das heisst, dass neue Knoten immer von links nach
rechts angehängt werden und angefangene Zeilen zuerst gefüllt werden, bevor eine neue
begonnen wird.
ƒ Daraus folgt, dass auf den Tiefen i=0..h-1 immer alle Knoten (2^i) vorhanden sind.
ƒ Es gibt maximal einen Knoten mit nur einem Kind, das ein linkes Kind sein muss.
ƒ Der letzte Knoten eines vollständigen Binärbaums ist somit der am weitesten rechts stehende
Knoten auf Stufe (Tiefe) h.
ƒ Höhe des Heaps: h = ⎣ lb(n) ⎦. (n = Anzahl Knoten, ⎣ x ⎦ = floor = abrunden).
Kälin Thomas, Abt I
14/35
06.07.2006
Programmieren 2
17.2. Implementierung einer Priority Queue mittels Heap
Mittels eines Heaps können wir eine Priority Queue implementieren. Wir speichern eine Entry (Key,
Value) in jedem Knoten des Heaps. Wegen der Bedingung, dass der tiefste Key immer im RootElement stehen muss, kann mittels min() / removeMin() in O(1) zugegriffen werden.
17.2.1.
Einfügen eines Elements
Beim Einfügen muss zuerst der „letzte Knoten“ gefunden werden. Dort wird das neue Element
eingefügt. Anschliessend muss eventuell die Heap-Eigenschaft mittels „Upheap“ hergestellt werden.
17.2.2.
Upheap-Verfahren (Bubbeling)
ƒ Nach dem Einfügen eines neuen Schlüssels k könnte die Heap-Ordnungseigenschaft verletzt sein.
ƒ Der Upheap-Algorithmus stellt die Ordnungseigenschaft wieder her, indem der neue Schlüssel k
entlang einem Pfad vom Einfügeknoten Richtung Wurzel mit dem jeweils darüberliegenden Knoten
vertauscht wird.
ƒ Upheap wird beendet, sobald der neue Schlüssel k entweder die Wurzel erreicht hat, oder falls die
Ordnungsrelation hergestellt ist.
ƒ Wegen der Höhe des Heaps benötigt das Verfahren O(log(n))-Zeit.
17.2.3.
Entfernen eines Elements
Beim Entfernen mittels removeMin() wird immer die Wurzel entfernt. Diese wird anschliessend durch
den Schlüssel des letzten Knotens ersetzt (vollständiger Binärbaum muss erhalten bleiben!). Folglich
ist natürlich die Ordnungsrelation verletzt, welche mittels „Downheap“ hergestellt werden muss.
17.2.4.
Downheap-Verfahren
ƒ Beim Ersetzen des Schlüssels in der Wurzel wird meistens die Heap-Eigenschaft verletzt.
ƒ Mittels Downlheap-Verfahren wird die Ordnungeigenschaft wieder hergestellt, indem der Schlüssel k
entlang einem Pfad in Richtung Äste vertauscht wird.
ƒ Downheap endet, wenn der Schlüssel in einem Blatt landet oder die Ordnungsrelation hergestellt ist.
ƒ Wegen der Höhe des Heaps benötigt das Verfahren O(log(n))-Zeit.
17.2.5.
Heap-Sort
Mit Hilfe einer Heap-basierten Priority Queue kann eine Sequenz von n Elementen innert O(n*log(n))Zeit sortiert werden, da insert() und removeMin() je O(log(n))-Zeit benötigen.
17.3. Implementierung mittels Vector
ƒ
ƒ
ƒ
ƒ
ƒ
Ein Heap mit n Knoten kann mit einem Vector der Länge n+1 realisiert werden.
Linkes Kind wird bei Position 2*i gespeichert, rechtes Kind bei 2*i+1.
Insert() entspricht dem Einfügen beim Position n+1.
removeMin() entspricht dem Entfernen bei Position 1.
Sortierung lässt sich mittels üblichen Verfahren (In-Place Sort) realisieren.
17.4. Konstruktion eines Heaps
17.4.1.
Top-Down
Beim Top-Down Verfahren wird der Heap von oben (Wurzel) nach unten (Blätter) aufgebaut. Dabei
wird aus einer Sequenz von Schlüsseln immer der niedrigste Wert ausgewählt und damit ein
vollständiger Binärbaum aufgebaut. Wegen der Suche nach dem niedrigsten Wert benötigt das
Verfahren O(n2) Zeit.
17.4.2.
Bottom-Up
Das Bottom-Up Verfahren wird in mehreren Phasen durchgeführt, wobei der Heap von unten (Blätter)
nach oben (Wurzel) aufgebaut wird. Es wird O(n) Zeit benötigt.
ƒ 1) Hälfte der Elementen (abgerundet) auswählen und platzieren.
ƒ 2) Hälfte der Elemente (abgerundet) auswählen und oberhalb der Elemente aus Schritt 1 platzieren.
ƒ 3) Bei den in Schritt 2 entstandenen Binärbäumen muss nun mittels Downheap-Verfahren die HeapEigenschaft wiederhergestellt werden.
ƒ Schritte 2 und 3 wiederholen, bis keine Elemente mehr vorhanden sind.
Kälin Thomas, Abt I
15/35
06.07.2006
Programmieren 2
18.
Map ADT
Eine Map modelliert eine durchsuchbare Collection von Schlüssel-Wert Entries. Jeder Schlüssel darf
nur einmal vorkommen. Anwendung beispielsweise in einem Adressbuch.
18.1. Operationen
v get(k): Liefert Value v zum Key k. Key nicht vorhanden: null.
v put(k,v): Setzt Value v bei Key k. Gibt alten Value bei Key zurück.
v remove(k): Liefert und entfernt Value z zum Key k.
int size(): Anzahl Elemente der Map
bool isEmpty(): Elemente in der Map vorhanden?
C keys(): Liefert iterierbare Collection mit allen Schlüsseln
C values(): Liefert iterierbare Collection mit allen Werten
C entries(): Liefert iterierbare Collection mit allen Entries
18.2. Implementierung mittels Linked-List
Mittels einer unsortierten Liste kann eine MAP effizient implementiert werden. Die Entries der Map
können in einer Liste (oder doppelt verketteten Liste) in beliebiger Reihenfolge abgespeichert werden.
Beim Abrufen, bzw. Einfügen von Entries wird jeweils über die ganze Liste iteriert und nach dem Key
gesucht. Put(), get() und remove() benötigen deshalb immer O(n) Zeit.
18.3. Sentinel-Trick
Beim Sentinel-Trick wird der gesuchte Knoten IMMER am Ende der Liste eingefügt. Wir können
deshalb bei der Iterationsschleife die Abfrage „hasNext()“ sparen, da wir sicher irgendwann den
gesuchten Wert finden werden. Wir müssen nur nach dem Auffinden des Wertes überprüfen, ob es
sich bei dem betreffenden Knoten um den Sentinel oder einen echten Eintrag handelt.
19.
Dictionary ADT
Das Dictionary ADT beschreibt eine Collection, bestehend aus Schlüssel-Wert Entries. Dabei darf, im
Gegensatz zur Map, der Key mehrmals vorkommen. Anwendung beispielsweise bei Wort-Definition
Paaren oder DNS-Listen.
19.1. Operationen
E find(k): Liefert ersten Entry mit Key k. Falls Key nicht vorhanden: null.
C findAll(k): Liefert iterierbare Collection der Entries mit dem Key k.
E insert(k,v): Fügt neue Entry mit Key k/Value v hinzu. Liefert neue Entry.
E remove(E): Entfernt und liefert Entry E.
C entries(): Liefert iterierbare Collection aller Entries.
int size(): Anzahl Elemente des Dictionary
bool isEmpty(): Elemente im Dictionary vorhanden?
19.2. Implementierung mittels Linked-List
Mit einer unsortierten (Double)-Linked-List können wir ein Dictionary implementieren. Insert()
benötigt dabei O(1) Zeit, da wir neue Entrys einfach am Ende der Liste anfügen. Find(), remove() und
findAll() benötigen immer O(n) Zeit, da jeweils die komplette Liste durchsucht werden muss.
19.3. Implementierung mittels Map
In einer Map werden Entries vom Typ <Schlüssel, Set> abgelegt. Im Set werden alle Werte, welche
zu einem Schlüssel gehören, abgelegt.
19.4. Binary Search
Falls das Dictionary mittels einem sortieren Array implementiert ist, können wir die Suchzeit auf
O(log(n)) reduzieren.
ƒ 1) Wert in der Mitte des Arrays auslesen
ƒ 2) Suchwert mit Wert aus Schritt 1 vergleichen. Falls der Suchwert kleiner ist, wird mit der linken
Hälfte des Arrays fortgefahren, andernfalls mit der rechten Seite.
ƒ 3) Wir fangen mit dem restlichen Teil des Arrays erneut bei Schritt 1 an.
Kälin Thomas, Abt I
16/35
06.07.2006
Programmieren 2
20.
Hash-Tabellen
Bei den Hash-Tabellen wird mittels einer Hash-Funktion h ein Key auf einen Integer in einem fixen
Interval abgebildet. Dieser Integer (=Hashwert des Keys) wird als Index in ein Array (Tabelle)
verwendet.
20.1. Hash-Funktionen
Eine Hash-Funktion besteht für gewöhnlich aus zwei Teilen, der Hash-Funktion und der KompressionsFunktion. Das Ziel der Hash-Funktion ist es, die Schlüssel möglichst zufällig zu verteilen. Die Aufgabe
der Kompressions-Funktion lautet, die generierten Schlüssel in ein fixes Intervall zu transformieren.
20.1.1.
Hash-Funktion
Grundfunktion: h1 = Key -> Integer
ƒ Eine Hash-Funktion heisst perfekt, wenn es keine Kollisionen gibt.
ƒ Horner-Schema: Ziel des Horner-Schemas ist es, die Anzahl Rechenoperationen zu reduzieren. Die
Laufzeit senkt sich dabei auf O(n).
//Grundmuster Horner-Schema
P(z)
= a0*z0 + a1*z1 + a2*z2 + a3*z3
= a0 + z(a1 + z(a2 + z(a3)))
//Beispiel Binärzahlen
P(1101) = 1*20 + 0*21 + 1*22 + 1*23 = 13
//3 Add, 7 Mul
= 1 + 2(0 + 2(1 + 2(1))) = 13
//2 Add, 3 Mul
20.1.2.
Kompressions-Funktion
Grundfunktion:
h2 = Integer -> [0, N-1]
Beispiel Modulo:
h2 = y % N oder h2 = (a*y+b) % N
ƒ Eine typische Kompressions-Funktion ist die Modulo-Division. Als Grösse der Hash-Tabelle wird oft
eine Primzahl gewählt, da dadurch die Hash-Funktion optimaler gewählt werden kann.
20.2. Kollisionsbehandlung
20.2.1.
Geschlossene Adressierung (offenes Hashverfahren)
Bei der geschlossenen Adressierung zeigt jede Zelle der Tabelle auf eine Liste. Dies wird auch
„Seperate Chaining“ genannt. Vorteil ist, dass die Verkettung einfach ist. Nachteilig hingegen, dass
die Verkettung Speicherplatz ausserhalb der Tabelle benötigt.
20.2.2.
Offene Adressierung (geschlossene Hashverfahren)
Bei der offenen Adressierung wird für kollidierende Elemente (Überläufer) mittels einer
Sondierungsfunktion ein Platz in der Nähe der belegten Zelle gesucht. Jede inspizierte Zelle wird als
„probe“ bezeichnet, deshalb auch der Name „Probing“.
ƒ Lineares Sondieren: Linear vorwärts nach freier Stelle suchen => +1, +2, +3, …
ƒ Lineares negatives Sondieren: Linear rückwärts nach freier Stelle suchen => -1, -2, -3, …
ƒ Quadratisches Sondieren: Quadratisch vorwärts nach freier Stelle suchen => +1, +4, +9, …
ƒ Alternierendes Sondieren: Linear abwechselnd nach freier Stelle Suchen => -1, +2, -3, …
ƒ Alternierendes quadr. Sondieren: Quadr. abwechselnd nach freier Stelle suchen => -1, +4, -9, …
Offene Adressierung erfordert eine besondere Behandlung der Lösch-Operationen. Soll ein Datensatz
gelöscht werden, so kann dies die Sondierungsfolge für einen anderen Datensatz unterbrechen. Zu
löschende Datensätze dürfen deshalb nicht physisch gelöscht, sondern nur als gelöscht markiert
werden.
20.2.3.
Doppeltes Hashing (Offene Adressierung)
Die Sondierungsfunktion (offene Adressierung) ist eine zweie Hashfunktion, deren Wert linear zur
ersten Hash-Funktion addiert wird.
20.3. Performance
ƒ Lastfaktor (a = Belegte Zellen / Totale Zellen) bestimmt Zeitverhalten. In der Praxis ist Hashing
sehr schnell, falls der Lastfaktor nicht zu hoch ist (a < 0.8).
ƒ Worst-Case: O(n) Zeit für Suchen, Einfügen und Löschen. Optimaler Zugriff in O(1) -> Index!
Kälin Thomas, Abt I
17/35
06.07.2006
Programmieren 2
21.
Skip-List
21.1. Definition einer Skip List
Eine Skip List besteht aus einer Serie von Listen (S0-SH). Jede dieser Listen enthält künstliche Anfangs(+∞) und Endknoten (-∞). Die unterste Liste (S0) enthält alle Keys in nicht absteigender
Reihenfolge, die oberste Liste (SH) nur den Anfangs- und Endknoten.
21.1.1.
Perfekte Skip List
Jede Liste enthält jeweils Knoten in der Mitte der Intervalle der Nachfolgerliste. Die perfekte Skip
List wird in Regel nicht verwendet, da der Verwaltungsaufwand zu gross ist.
21.1.2.
Random Skip List
Anders als bei der perfekten Skip List, wird die Höhe für die einzelnen Listenelemente zufällig
bestimmt. Dabei wird versucht, die Höhen gleichmässig und zufällig über die Liste zu verteilen. Je
weiter oben die Liste sich befindet, desto weniger Knoten soll sie enthalten.
21.2. Java-Code
21.2.1.
SkipListNode
ƒ Ein Node in einer Skiplist stellt einen „Turm“ der Struktur dar. Höhe kann aus dem Array „next“
herausgelesen werden.
class SkipListNode {
int key;
//Enthält Schlüssel des Nodes
SkipListNode[] next;
//Referenzen auf nächsten Node jedes Levels
}
21.2.2.
SkipList
class SkipList {
int maxHeight;
int height;
SkipListNode head;
SkipListNode tail;
SkipListNode[] update;
//Maximale Höhe der Liste
//Aktuelle Höhe der Liste
//Kopf der Liste
//Ende der Liste
//Hilfsarray, siehe „Einfügen“
SkipList() {
maxHeight = 5;
//Höhe geht von 0-5! -> 6 Level!
height = 0;
update = new SkipListNode[maxHeight + 1];
head = new SkipListNode(Integer.MIN_VALUE,maxHeight);
tail = new SkipListNode(Integer.MAX_VALUE,0);
//Stelle Grundverknüpfungen her
for (int i=0; i <= maxHeight; ++i) { head.next[i] = tail; }
}
}
21.2.3.
Suchen
search (int key) {
//Wir starten im Head beim höchsten Level
SkipListNode p = head;
for (int i = height; i>= 0; i--) { //Einzelne Levels durchgehen
while (p.next[i].key < key) {
p = p.next[i];
}
}
//Der nächste Knoten ist vermutlich der gesuchte Wert!
p = p.next[0];
if (p.key == key && p != tail) { return p; }
else { return null; }
}
Kälin Thomas, Abt I
18/35
06.07.2006
Programmieren 2
21.2.4.
Einfügen
insert (int key) {
//Empfohlenerweise zuerst Höhe bestimmen!
int newheight = randheight();
if (newheight > height) { height = newheight; }
//Wir suchen alle Vorgänger und speichern diese im Hilfsarray
SkipListNode p = head;
for (int i = height; i >= 0; i--) {
while (p.next[i].key < key) { p = p.next[i]; }
update[i] = p;
}
//Wir stellen die Verkettungen für den neuen Knoten her
p = new SkipListNode(key, newheight);
for (int i = 0; i <= newheight; i++) {
p.next[i] = update[i].next[i];
update[i].next[i] = p;
}
}
21.2.5.
Löschen
delete(int key) {
//Wir suchen alle Vorgänger und speichern diese im Hilfsarray
SkipListNode p = head;
for (int i = height; i >= 0; i--) {
while (p.next[i].key < key) { p = p.next[i]; }
update[i] = p;
}
//Vermuteten Knoten überprüfen
p = p.next[0];
if (p.key != key) { return null; } //Schlüssel nicht gefunden
//Wir verketten die Elemente neu, zu löschenden Knoten „überspringen“!
for (int i = 0; i < p.next.length; i++) {
update[i].next[i] = update[i].next[i].next[i];
}
//Leere Levels löschen
while(height >= 0 && head.next[height] == tail) { height--; }
}
21.2.6.
Zufallsgenerator
int randheight() {
//Wahrscheinlichkeit ist 0.5 i
height = 0;
while (rand() % 2 == 0 && height < maxHeight) { height++; }
return height;
}
21.3. Implementierung mittels Quad-Nodes
Eine Skip List könnte auch mittels einem Quad-Knoten implementiert werden.
21.4. Analysen
21.4.1.
Speicherplatz
Der Speicherbedarf einer Skip List hängt vom zufälligen Einfügen ab. Die Wahrscheinlichkeit für einen
Entry in der Liste SI ist 1/2i. Die erwartete Grösse der Liste SI ist n*p, also n/2i. Über alle Stufen
aufsummiert ergibt das einen Speicherplatz < 2n, also O(n). (Siehe Grafik)
21.4.2.
Höhe
Die Skip List besitzt eine Höhe von O(log n).
21.4.3.
Laufzeiten
Suchen, Einfügen und Löschen benötigt O(log n) Zeit.
Kälin Thomas, Abt I
19/35
06.07.2006
Programmieren 2
22.
Binäre Suchbäume
Ein binärer Such-Baum ist ein binärer Baum, welcher Entries vom Typ Key/Value in
seinen internen Knoten speichert. Die Blätter speichern keine Daten. Ausserdem
muss die Bedingung erfüllt sein, dass die Inorder-Traversierung die Keys in nicht
absteigender Reihenfolge besucht. Das heisst also:
o Linkes Kind <= Knoten <= Rechtes Kind
22.1. Höhe des Baums
Die Höhe ist im besten Fall O(log(n)). Dabei ist der Baum völlig ausbalanciert. Im schlechtesten Fall
kann die Höhe jedoch auch O(n) sein, heisst also, dass auf jeder Ebene des Baums nur ein Knoten
liegt. Wir haben also so etwas ähnliches wie eine verkettete Liste.
22.2. Implementierung
Die Implementierung eines Knotens ist denkbar einfach: Ein Knoten aggregiert
intern zwei Knoten (linkes / rechtes Kind) und zusätzlich ein Entry (Key/Value).
Im Baum selber wird nur ein Knoten für die Wurzel und die Zugriffsmethoden
gespeichert.
22.3. Suche
TreeSearch(Key, Knoten) {
if (Knoten.isExternal()) { return null; }
//Element nicht gefunden
if (Key<Knoten.Key) { return TreeSearch(Key, Knoten.leftChild()); }
else if (Key>Knoten.Key) { return TreeSearch(Key, Knoten.rightChild()); }
return Knoten;
//Aktueller Knoten ist gesuchtes Element!
}
22.4. Einfügen
Wir gehen analog zur Suche vor. Jedoch wird bei der isExternal()-Abfrage kein null zurückgegeben,
sondern ein neuer Knoten passend zum Key generiert.
22.5. Löschen
Beim Löschen müssen wir 3 Fälle unterscheiden. Der Knoten v mit Schlüssel k ist…:
ƒ Ein Knoten mit zwei Blättern (Bsp.: 1). Hierbei ist das Löschen einfach. Der Knoten kann entfernt
und durch „null“ ersetzt werden.
ƒ Ein Knoten mit einem Blatt (Bsp.: 9). Hier kann der zu löschende Knoten durch den Kind-Knoten
ersetzt werden, der kein Blatt ist.
ƒ Ein Knoten ohne Blätter (Bsp.: 2). Dies ist etwas aufwändiger. Der zu löschende Knoten muss durch
den Knoten ersetzt werden, der in der Inorder-Traversierung als nächstes folgen würde (siehe
Hilfsmethode). Der Knoten, der den Platz des zu löschenden Knotens übernimmt, muss im Anschluss
auch wieder den Löschen-Algorithmus durchlaufen.
22.5.1.
Symetrischer Nachfolger
ƒ Hilfsmethode zum Auffinden des Inorder-Nachfolgers.
Knoten SymNach(Knoten p) {
if (p.rightson.leftson != null) {
p = p.rightson;
while (p.leftson.leftson != null) { p = p.leftson; }
}
}
22.6. Inorder-Methode
ƒ Die Hilfsmethode führt eine Inorder-Traversierung durch
void Inorder(Knoten p) {
if (p.leftson != null) { Inorder(p.leftson); }
System.out.print(„(„ + p.getKey() + „)“);
if (p.rightson != null) { Inorder(p.rightson); }
}
Kälin Thomas, Abt I
20/35
06.07.2006
Programmieren 2
23.
AVL Baum
Ein AVL Baum ist ein binärer Suchbaum, der versucht die Verteilung der Knoten zu balancieren.
Deshalb muss für jeden internen Knoten v von T gelten, dass die Höhe der Kinder von v sich
höchstens um 1 unterscheiden. Für die Höhe eines Baums gilt O(log(n)).
23.1. Balance
B(Knoten) = |Höhe(Links) – Höhe(Rechts)|
Falls nach dem Einfügen eines neuen Knotens B(K) >= 2 ist, muss der Baum umstrukturiert werden.
Den Prozess des Ausgleichens bezeichnet mal als Rotation.
23.2. Einfügen
Das Einfügen erfolgt wie beim binären Suchbaum. Es wird also immer ein externer Knoten expandiert.
Nach dem Einfügen muss vom Knoten in Richtung Wurzel traversiert werden. Bei jedem Vorgänger
muss die Balance neu berechnet werden. Ist der Baum nicht mehr balanciert, muss ein Ausgleich
vorgenommen werden.
23.2.1.
17
N
N
Cut/Link Restrukturierungs-Algorithmus
Die einfachste Methode zur Ausbalancierung ist der Cut/Link-Algorithmus. Ich erläutere diesen anhand
eines Beispiels.
44
Der nebenstehende Baum ist offensichtlich unbalanciert. Nehmen wir an,
wir hätten gerade 88 eingefügt.
62
1) 1. Unbalancierten Knoten von (88) Richtung Wurzel suchen: (44) = Z
50
78
2) Kind von Z mit grössere Höhe: (62) = Y
3) Kind von Y mit grösserer Höhe: (78) = X
48
54
88
4) T0-T3 festlegen (restliche Subbäume!). Nummerierung von LnR!
5) Knoten X-Z nach Inorder-Reihenfolge mit A-C benennen
N
N
N
N
N
N
N
Wir erhalten den links abgebildeten Baum. Beim Programmieren werden
diese 7 Elemente nach folgendem Schema in einem Array abgelegt.
T0
A
T1
B
T2
C
T3
Aus diesem Muster kann jetzt der neue Baum gemäss folgendem Muster
gezeichnet werden:
Hier sehen wir nun den komplett umstrukturierten Baum. Diese Methode
führt zum selben Ergebnis wie die normalen (Doppel-)Rotationen! Die
Umstrukturierung kann eventuell eine neue Unbalance generieren.
Deshalb unbedingt weiter bis zur Wurzel überprüfen!
23.3. Löschen
Das Löschen erfolgt nach demselben Prinzip wie bei den binären Suchbäumen. Natürlich muss auch
hier wieder die Balance des Baumes überprüft werden.
23.4. Laufzeiten
ƒ Eine einzelne Restrukturierung benötigt O(1) Zeit, unter Benutzung eins verlinkten Binärbaums
ƒ find(), insert() und delete() sind O(log(n)), da bei jeder Aktion zuerst durch den Baum gesucht
werden muss, was durch die Höhe des Baums (log(n)) bestimmt wird.
Kälin Thomas, Abt I
21/35
06.07.2006
Programmieren 2
24.
Merge Sort
Merge Sort ist ein Sortier-Algorithmus basierend auf dem Teile-und-Herrsche Paradigma (Divide and
Conquer). Die Laufzeit beträgt O(n log(n)). Ein grosser Vorteil des Merge-Sort ist, dass dessen
Verhalten absolut stabil ist. Die Sortierreihenfolge der Eingangssequenz hat auf die Laufzeit überhaupt
keinen Einfluss.
24.1. Code
Sequenz mergeSort(Sequenz S) { | merge(Sequenz S1, Sequenz S2) {
if (S.size() > 1) {
| Sequenz S = new Sequenz(); //Temp
(S1,S2) = Teile(S,n/2);
| while (!S1.empty() && !S2.empty()) {
S1 = mergeSort(S1);
|
if (S1.first() < S2.first()) {
S2 = mergeSort(S2);
|
S.insert(S1.remove(S1.first());
S = merge(S1,S2);
|
} else {
}
|
S.insert(S2.remove(S2.first());
return S;
|
}
}
| }
| while (!S1.isEmpty()) {
|
S.insert(S1.remove(S1.first());
| }
| while (!S2.isEmpty()) {
|
S.insert(S2.remove(S2.first());
| }
| return S;
25.
Quick-Sort
Quick-Sort ist ein Sortier-Algorithmus basierend auf Divide-and-Conquer. Aus einer gegebenen
Sequenz wird ein Element ausgewählt, das sog. Pivot. Die restlichen Elemente werden jeweils mit
diesem Pivot verglichen und abhängig davon in eine der 3 Gruppen (Less, Equals, Greater) eingeteilt.
Diese drei Gruppen werden dann wieder mit diesem Verfahren behandelt (Rekursion).
25.1. Laufzeit
Die Laufzeit des Algorithmus ist stark abhängig vom gewählten Pivot. Der Worst-Case tritt dann auf,
wenn das Pivot auf jeder Rekursionsstufe genau das Minimum- oder Maximum-Element ist. In diesem
Fall tritt eine Laufzeit von O(n2) auf, da auf jeder Ebene alle Elemente ausser dem Pivot in dieselbe
Gruppe (Less oder Greater) eingeteilt werden. Im Best-Case beträgt die Laufzeit O(n log(n)).
Nachfolgend noch einige Beispiele:
ƒ Sortiere Sequenz -> (1,2,3,4): Falls das letzte oder erste Element als Pivot gewählt wird: O(n2)!
ƒ Gleiche Elemente -> (1,1,1,1): Auch O(n2), da alle Elemente nach „Equals“ gehen.
25.2. Beispiele
Binärbaum
Auf jeder Ebene wird von „links nach rechts“
gelesen. Beispiel Wurzel: Zuerst werden alle
Elemente vom linken Kind gelesen und vor das Pivot
gehängt (2, 4). Anschliessend kommt das Pivot (6),
darauf alle Elemente vom rechten Kind (7, 9).
Kälin Thomas, Abt I
1.
2.
3.
4.
5.
6.
7.
8.
9.
In-Place
2
4
1
7
4
9
4
9
2
4
1
7
4
9
2
4
1
7
1
9
2
4
4
7
1
9
2
4
4
7
1
9
2
4
4
7
1
2
9
4
4
7
Pivot wird festgelegt
Suche von links bis Elemente > 2 gefunden -> 4
Suche von rechts bis Element < 2 gefunden -> 1
Tausche Elemente 4 und 1
Suche von links bis Elemente >2 gefunden -> 9
Suche von rechts bis Pivot erreicht -> 2
Tausche Element 9 mit Pivot
Pivot erreicht Endposition (rot).
Mit linker / rechter Seite von Pivot fortfahren
22/35
06.07.2006
Programmieren 2
26.
Bucket-Sort
ƒ Beim Bucket-Sort wird in einer ersten Phase eine Sequenz von (Key, Value)-Elementen auf
verschiedene Bereiche (Buckets = Eimer) verteilt. In der zweiten Phase werden diese einzelnen
Container der Reihe nach ausgelesen und zu einer neuen Sequenz zusammengefügt.
ƒ Die Phase 1 benötigt O(n) Zeit, Phase 2 benötigt O(n+N) Zeit, wobei N die Anzahl Container ist.
ƒ Stabile Sort Eigenschaft: Die relative Ordnung von zwei Items mit demselben Key werden durch den
Algorithmus nicht verändert, falls keine Zwischenphase (Sortierung) durchgeführt wird.
ƒ Eine Variante des Bucket-Sort besteht darin, dass zwischen Phase 1 und Phase 2 eine Sortierung der
Elemente in einem Bucket erfolgt.
ƒ Eine Anwendung des Bucket-Sort wäre z.B. eine Namensliste sortieren. Jeder Bucket ist hierbei ein
Buchstabe aus dem Alphabet und der Key ist der Anfangsbuchstabe eines Namens.
27.
Radix-Sort
ƒ Radix-Sort ist eine Spezialisierung des lexikographischen Sortierens (Sortieren nach Dimensionen),
welcher Bucket-Sort als stabilen Sortier-Algorithmus für jede Dimension benutzt.
ƒ Radix-Sort läuft in O(d*(n+N)) Zeit, wobei d für die Anzahl Dimensionen steht.
27.1. Beispiel: Sortierung von 3-Bit Integers
1
111
001
100
28.
2
100
111
001
3
100
001
111
4
001
100
111
1) Ursprungs-Zustand der ungeordneten Elemente.
2) Es wurde mit Bucket-Sort nach der 1.Bit-Position sortiert. Dabei wurde
die relative Ordnung (Stabilität) der Elemente nicht verändert.
3) Sortierung mit Bucket-Sort nach der 2.Bit-Position.
4) Sortierung mit Bucket-Sort nach der 3.Bit-Position. Da wir nun alle
Dimensionen (Bitpositionen) durchlaufen haben, sind wir am Ende des
Radix-Sorts angelangt. Die Bitfolgen sind sortiert.
Untere Grenze der Sortierung
ƒ Zur Herleitung einer unteren Grenze (Lower Bound) der Laufzeit zählen wir die Anzahl Vergleiche.
Jeder mögliche Durchgang ergibt einen Entscheidungsbaum. Die Höhe des Entscheidungsbaumes
entspricht der unteren Grenze der Laufzeit.
ƒ Jeder vergleichsbasierte Sortier-Algorithmus hat im Worst-Case als untere Grenze die Laufzeit von
Ω(n*log(n)).
29.
Sets
Ein Set wird durch eine sortierte Sequenz seiner Elemente repräsentiert.
29.1. Grundoperationen
Alle Set-Operationen können mit generischem Mischen implementiert werden. Die Laufzeit einer
Operation mit den Sets A und B sollte maximal O(nA + nB) sein, daraus folgt O(n).
29.1.1.
Vereinigung (Union)
Es wird immer das kleine Element ins neue Set eingefügt.
A.first() < B.first()
-> V.insertLast(A.removeFirst())
A.first() > B.first()
-> V.insertLast(B.removeFirst())
A.first() = B.first()
-> V.insertLast(A.removeFirst()); B.removeFirst();
29.1.2.
Durchschnitt (Intersection)
Es werden nur gleiche Elemente
A.first() < B.first()
A.first() > B.first()
A.first() = B.first()
29.1.3.
eingefügt.
-> A.moveNext();
-> B.moveNext();
-> V.insertLast(A.removeFirst()); B.removeFirst();
Differenz (Subtraction)
Ersetze A durch Differenz von A und B. Es werden also nur Elemente eingefügt, welche nicht in beiden
Mengen vorkommen.
Kälin Thomas, Abt I
23/35
06.07.2006
Programmieren 2
29.2. Operationen
bool add(E): Element hinzufügen, falls noch nicht vorhanden
bool addAll(C): Alle Elemente einer Collection hinzufügen.
bool remove(E): Element aus dem Set entfernen
bool removeAll(C): Alle Elemente in der Collection aus dem Set entfernen
bool retainAll(C): Löscht alle Elemente, die nicht in der Collection sind.
bool contains(E): Überprüft, ob ein Element im Set vorhanden ist.
bool containsAll(C): Überprüft ob ALLE Elemente vorhanden sind.
void clear(): Alle Elemente des Sets löschen.
bool isEmpty(): Elemente im Set vorhanden?
int size(): Anzahl Elemente des Sets
E[] toArray(): Erzeugt ein Array aus dem Set
29.3. Set-Varianten in Java
AbstractSet, EnumSet, HashSet, LinkedHashSet, TreeSet (sortiert!).
30.
Automaten
30.1. Akzeptor
Ein Akzeptor hat die Aufgabe, eine Folge von Eingaben auf Ihren Erfolg / Gültigkeit zu prüfen. Ist der
Automat nach Abarbeitung einer Eingabe in einem Endzustand, war die Eingabe gültig. Ein Akzeptor
besitzt keine Ausgabe.
Q = {z0,z1,z2}
Σ = {a,b}
δ = (z0,a,z0) (z0,b,z1) (z1,a,z0) (z1,b,z2) …
q0 = z 0
F = {z2}
30.2. Transduktor
Ein Transduktor hat die Aufgabe, auf eine Folge von Eingaben eine definierte Ausgabe zu erzeugen. Er
besitzt keine Endzustände.
30.2.1.
Mealy / Moore – Automaten
Moore- und Mealy-Automaten sind gleichwertig. Der eine kann in den jeweils anderen überführt
werden. In der Praxis werden meistens Mischmodelle benutzt.
Mealy
Moore
Im Mealy-Modell werden Eingabeaktionen
benutzt, die Ausgabe hängt also vom Zustand
und der Eingabe ab.
31.
Im
Moore-Modell
werden
nur
die
Eingangsaktionen verwendet, die Ausgabe hängt
also nur vom Zustand ab.
Pattern Matching
31.1. Allgemeines
ƒ Substring: Ein Substring P[i..j] von P ist die Subsequenz von P, bestehend aus den Zeichen mit Rang
zwischen und inklusiv i und j.
ƒ Präfix: Ein Präfix von P ist ein Substring vom Typ P[0..i]
ƒ Suffix: Ein Suffix von P ist ein Substring vom Typ P[i..m-1]
Kälin Thomas, Abt I
24/35
06.07.2006
Programmieren 2
31.2. Brute-Force
Beim Brute-Force Algorithmus wird das Such-Pattern einfach immer um eine Position vorgeschoben,
falls keine Übereinstimmung gefunden wurde. Der Algorithmus benötigt O(n*m) Zeit, wobei m die
Länge des Patterns ist.
31.2.1.
Worst-Case Beispiel
T = aaaaaaaaaaaaaaaaaaaaaah
|||
P = aaah ->
P = aaah ->
P =
aaah -> …
Bei jeder Position von T werden jeweils 3 a’s verglichen,
bevor an der 4. Position eine Nicht-Übereinstimmung (h)
gefunden wird. Dies wird für alle Positionen von T so
fortgeführt. Da wir für n-Zeichen von T m-Vergleiche (m
ist die Länge des Patterns) durchführen müssen, ergibt
sich O(n*m).
31.3. Boyer-Moore
Der Boyer-Moore Algorithmus basiert auf zwei Heuristiken:
ƒ Looking-Glass: Vergleiche Pattern P mit einer Subsequenz von T. Starte dabei am ENDE des
Patterns.
ƒ Character-Jump:
Fall 1
Fall 2
Falls bei einem Vergleich das Zeichen c aus Kommt c in P nicht vor, verschiebe P aufs
dem Text T im Pattern P vorkommt, verschiebe nächste Feld nach c in T.
P bis das letzte Auftreten von c in P mit c in T
übereinstimmt.
Achtung: Nicht immer liefert die „Schlechtes-Zeichen“-Strategie ein gutes Eregnis. In Sonderfällen
kann eine negative Verschiebung entstehen. Dies kann umgangen werden, indem man in diesen
Fällen stattdessen umf 1 nach vorne schiebt.
31.3.1.
Last-Occurrence Funktion
Boyer-Moore’s Algorithmus analysiert zuerst das Pattern P und das Alphabet Σ, um auf die „lastoccurence“-Funktion L aufzubauen. Die Funktion lässt sich in O(m+s) berechnen, wobei m die Länge
des Patterns und s die Anzahl Zeichen in Σ ist.
S = {a, b, c, d}
c
a
b
c
d
P = abacab
L(c)
4
5
3
-1
31.3.2.
Berechnung der Verschiebung
ƒ Das Zeichen T[i] kommt im Pattern P vor (Fall 1).
i = i + m – (last(T[i]) + 1)
i = Indexpos., m = Patternlänge
i = 0123456789
-> i = i + m – (last(T[i]) + 1)
T = aabababacb
-> i = 3 + 5 – (last(a) + 1)
P = cabbb
-> i = 3 + 5 – (1 + 1) = 3 + 5 – 2 = 6
cabbb -->
ƒ Das Zeichen T[i] kommt im Pattern P vor, ist allerdings schon vorbei (Ausnahme).
i = i + m – j
i = Indexpos., m = Patternlänge, j = Pos. im Pattern
i = 0123456789
-> i = i + m - j
T = aabababacb
-> i = 3 + 5 - 2
P = cabba
-> i = 6
cabba -->
ƒ Unviversal-Methode
i = i + m – ( min(j,last(T[i]) + 1)
Wir nehmen entweder j oder die Lastfunktion, je nach dem, welches den
tieferen Wert liefert.
Kälin Thomas, Abt I
25/35
06.07.2006
Programmieren 2
31.3.3.
Analyse
Der Boyer-Moore-Algorithmus benötigt O(n*m + s) Zeit, wobei n die Länge des Textes, m die Länge
des Patterns und s die Anzahl Zeichen des Alphabets sind. Im Worst-Case (T = aa…a, P = baaa)
funktioniert der Algorithmus genau wie Bruteforce, da immer nur um eine Stelle verschoben wird.
31.3.4.
Zusammenfassung
ƒ Analyse beginnt mit dem Ende des Patterns
ƒ Algorithmus verwendet Last-Occurence Funktion
ƒ Zwei Fälle: Verschiebung mit Hilfe der Last-Funktion oder Verschiebung um 1 (Brute-Force!)
31.4. KMP-Algorithmus
Der Knurr-Morris-Pratt Algorithmus vergleich das Muster gegen den Text von links-nach-rechts, aber
schiebt das Muster intelligenter als beim Brute-Force.
T = abaabx
P = abaaba
j = F(j-1) = F(5-1) = F(4) = 2 -> Neue Pos im Pattern
abaaba -->
31.4.1.
KMP-Fehlfunktion
In einer Vorlaufsphase sucht der Algorithmus Übereinstimmungen vom Präfix des Musters im Muster
selbst. Die Funktion kann in O(m) Zeit berechnet werden, wobei m die Länge des Patterns ist.
P[0] = a
-> 0
-> j
= 0 1 2 3 4 5
P[1] = ab
-> 0
P[j] = a b a a b a
P[2] = aba
-> 1
F(j) = 0 0 1 1 2 3
P[3] = abaa
-> 1
P[4] = abaab
-> 2
P[5] = abaaba
-> 3
31.4.2.
Beispiel
31.4.3.
Analyse
Der KMP-Algorithmus benötigt im optimalen Fall O(m + n) Zeit, wobei m die Länge des Patterns und n
die Länge des Suchtextes ist.
31.4.4.
Zusammenfassung
ƒ Analyse beginnt mit dem Anfang des Patterns
ƒ Verwendet Failure-Funktion, welche mit Hilfe der Ränder des Patterns berechnet wird.
32.
REGEX
ƒ Reguläre Ausdrücke werden an verschiedenen Orten verwendet: Compilerbau, Suchprogramme, …
ƒ Reguläre Ausdrücke unterstützen genau 3 Operationen: Alternative (ODER), Aneinanderreihung
(UND) und Wiederholung (Kleene-Star).
32.1. Metazeichen
Metazeichen beschreiben den regulären Ausdruck und werden interpretiert.
( ) [ ] { } \ ^ $ | ? * + .
Kälin Thomas, Abt I
26/35
06.07.2006
Programmieren 2
32.2. Tabellen
[abc]
[^abc]
[a-zA-Z]
[a-d[m-p]]
[a-z&&[def]]
[a-z&&[^bc]]
[a-z&&[^m-p]]
Character Classes
a, b oder c
Beliebiger Char ausser a,b,c
a bis z oder A-Z
a bis d oder m bis p. ([a-dm-p])
d, e oder f. (Durchschnitt)
a bis z ohne b und c. ([ad-z])
a bis z nicht m bis p ([a-lq-z])
^
$
\b
\B
\A
\G
\Z
\z
Boundary Matchers
Zeilenanfang
Zeilenende
Wortgrenze
Grenze (nicht eines Wortes)
Beginn der Eingabe
Ende des vorgängigen Matches
Ende der Eingabe (aber nicht Datei)
Ende der Eingabe
.
\d
\D
\s
\S
\w
\W
Predefinied Character Classes
Beliebiges Zeichen
Ziffer: [0-9]
Zeichen ausser Ziffer: [^0-9]
Whitespace: [\t\n\x0b\f\r]
Zeichen ausser Whitespace: [^\s]
Wort: [a-zA-Z_0-9]
Zeichen ausser Wort: [^\w]
Quantifiers
Posses.
Meaning
X?+
X, einmal oder nie
X*+
X, kein oder mehrfach
X, einmal oder
X+
X+?
X++
mehrfach
X{n}
X{n}?
X{n}+ X, genau n Mal
X{n,} X{n,}? X{n,}+ X, mindestens n Mal
X, mindestens n aber
X{n,m} X{n,m}? X{n,m}+
nicht mehr als m Mal.
Greedy
X?
X*
Lazy
X??
X*?
32.3. Quantoren
ƒ Greedy: Versucht, so viele Zeichen wie möglich zu erfassen.
ƒ Lazy (Reluctant): Versucht, so wenig Zeichen wie möglich bei einem Match zu erfassen.
ƒ Possesive: Versucht genau einmal, ob eine Übereinstimmung vorliegt.
32.3.1.
Beispiele
GROSSPAPAGROSSMAMAPAPAMAMA (25 Zeichen)
Greedy:
Lazy:
Possesive:
.*MA -> Match von 0..26
.*?MA -> Match bei 0..16, 16..18, 18..24, 24..26
.*+MA -> Muster wird nicht gefunden (nur ein Match!)
32.4. Java Beispiel
import java.util.regex;
public static String find(String strPattern, CharSequence strInput) {
Pattern pattern = Pattern.compile(strPattern); //Statische Klassenmethode
Matcher matcher = pattern.matcher(strInput);
if (matcher.find()) { return matcher.group(); }
return null;
}
//Greedy
strMatch = find(„A.*c“,“AbcAbc“);
//AbcAbc
strMatch = find(„A.+“,“AbcAbc“);
//AbcAbc
//Non-Greedy
strMatch = find(„A.*?c“,“AbcAbc“);
//Abc
strMatch = find(„A.+?“,“AbcAbc“);
//Abc
33.
Graphen
33.1. Begriffe
ƒ
ƒ
ƒ
ƒ
ƒ
Gerichtete Kanten: Geordnetes Paar von Vertices. 1.Knoten entspricht dem Start, 2.Knoten dem Ziel
Ungerichtete Kanten: Ungeordnetes Paar {V1,V2} von Vertices.
Gerichteter Graph: Alle Kanten sind gerichtet
Ungerichteter Graph: Alle Kanten sind ungerichtet
Endknoten: V ist Endknoten von a und b
Kälin Thomas, Abt I
27/35
06.07.2006
Programmieren 2
ƒ
ƒ
ƒ
ƒ
ƒ
ƒ
ƒ
ƒ
ƒ
ƒ
ƒ
ƒ
ƒ
ƒ
Inzident: a, d und b sind inzident (enden in) in V
Adjazente: U und V sind adjazent (benachbart)
Grad: Anzahl indizenter Kanten. Grad von X ist 5
Parallele Kanten: h und i sind parallele Kanten
Schleife: j ist eine Schleife
Pfad: Sequenz von alternierenden Knoten und Kanten. Bsp: (U,c,W,e,X,g,Y,f,W,d,V)
Einfacher Pfad: Alle Knoten und Kanten sind unterschliedlich. Bsp: (V,b,X,h,Z)
Zyklus: Zirkuläre Sequenz alternierender Knoten und Kanten. Bsp: (U,c,W,e,X,g,Y,f,W,d,V,a)
Einfacher Zyklus: Alle Knoten und Kanten sind unterschiedlich. Bsp: (V,b,X,g,Y,f,W,c,U,a)
Subgraph: Die Kanten und Knoten eines Subgraphes sind Teilmengen des ursprünglichen Graphen.
Aufspannender Subgraph: Der Subgraph enthält alle Knoten des ursprünglichen Graphen.
Verbundener Graph: Zwischen jedem Paar von Knoten existiert ein Pfad
Baum: Ein Graph, der verbunden ist und keine Zyklen aufweist
Aufspannender Baum: Ein aufspannender Subgraph, der auch ein Baum ist.
33.2. Operationen
V[] endVertices(E): Array mitden beiden Endknoten V einer Kante E
V opposite(V,E): Gegenüberliegender Endknoten von V bei Kante E
bool areAdjacent(V1,V2): Überprüft, ob zwei Knoten benachbart sind
void replace(V,O): Inhalt des Knoten V mit Element O ersetzen
void replace(E,O): Inhalt der Kante E mit Element O ersetzen
void insertVertex(O): Knoten mit Element O hinzufügen
void insertEdge(V1,V2,O): Kante von V1 nach V2 mit Element O hinzufügen
void removeVertex(V): Knoten V und die inzident Kanten entfernen
void removeEdge(E): Kante E entfernen
Coll incidentEdges(V): Collection der inzident Kanten des Knoten V
Coll vertices(): Collection aller Knoten im Graph
Coll edges(): Collection aller Kanten im Graph
33.3. Kanten-Listen Struktur
ƒ Knoten-Objekt: Besteht aus einem Element (Inhalt) und einer
Referenz auf die Position in der Knoten-Sequenz
ƒ Kanten-Objekt: Besteht aus einem Element (Inhalt), einer
Referenz auf den Ursprungsknoten, einer Referenz auf den
Zielknoten und einer Referenz auf die Postion in der Kantenfolge.
ƒ Knoten-Sequenz: Sequenz der einzelnen Knotenobjekte (gelb).
ƒ Kanten-Sequenz: Sequenz der einzelnen Kantenobjekte (blau).
33.4. Adjadenz-Listen Struktur
ƒ Knoten-Objekt: Besteht aus einem Element (Inhalt), einer
Referenz auf die Position in der Knoten-Sequenz und einer
Referenz auf eine Sequenz der inzidenten Kanten (grüne Pfeile).
ƒ Kanten-Objekt: Besteht aus einem Element (Inhalt), einer
Referenz auf den Ursprungsknoten, einer Referenz auf den
Zielknoten, einer Referenz auf die Postion in der Kantenfolge, zwei
Referenzen auf die Listenelemente der Endknoten (grüne Pfeile).
ƒ Knoten-Sequenz: Sequenz der einzelnen Knotenobjekte (gelb).
ƒ Kanten-Sequenz: Sequenz der einzelnen Kantenobjekte (blau).
Kälin Thomas, Abt I
28/35
06.07.2006
Programmieren 2
33.5. Adjazenz-Matrix Struktur
ƒ Knoten-Objekt: Besteht aus einem Element (Inhalt), einer
Referenz auf die Position in der Knoten-Sequenz und einem
Integer Key, der als Index in eine Tabelle dient
ƒ Kanten-Objekt: Besteht aus einem Element (Inhalt), einer
Referenz auf den Ursprungsknoten, einer Referenz auf den
Zielknoten und einer Referenz auf die Postion in der Kantenfolge.
ƒ 2D-Adjazenz-Array: Referenziert auf die Kantenobjekte für
adjazente (benachbarte) Knoten. Falls keine Kante vorhanden ist,
wird null eingetragen.
ƒ Knoten-Sequenz: Sequenz der einzelnen Knotenobjekte (gelb).
ƒ Kanten-Sequenz: Sequenz der einzelnen Kantenobjekte (blau).
33.6. Performance
ƒ Rahmenbedingungen: n Knoten, m Kanten, keine parallen Kanten, keine Schleifen
Kanten-Liste
Adjazenz-Liste
Adjazenz-Matrix
Speicherbedarf
n+m
n+m
n2
incidentEdges(V)
m
deg(V)
n
areAdjacent(V1,V2)
m
min(deg(V1), deg(V2))
1
insertVertex(O)
1
1
n2
insertEdge(V1,V2,O)
1
1
1
removeVertex(V)
m
deg(V)
n2
removeEdge(E)
1
1
1
33.7. Adjazenz-Matrix
b
(A) --- (B)
|
|
a |
| c
|
|
(C) --- (D)
d
34.
->
->
->
->
|0
|1
|1
|0
1
0
0
1
1
0
0
1
0|
1|
1|
0|
->
->
->
->
|0
|b
|a
|0
b
0
0
c
a
0
0
d
0|
c|
d|
0|
Tiefensuche (Depth-First Search: DFS)
DFS ist eine allgemeine Technik für die Traversierung eines Graphen. Sie entspricht in etwa der EulerTour bei binären Bäumen und läuft in O(n+m) Zeit ab, sofern n Knoten und m Kanten vorhanden
sind. Dabei bilden die markierten, besuchten Kanten einen aufspannenden Baum.
34.1. DFS-Algorithmus
In einer Initialisierungsphase werden alle Knoten und Kanten mit dem Label „UNEXPLORED“
initialisiert. Danach läuft der folgende Algorithmus (rekursiv) ab:
DFS(Graph G, Knoten V) {
setLabel(V, VISITED);
foreach (Kante E : G.incidentEdges(V)) {
if (getLabel(E) == UNEXPLORED) {
Knoten V2 = opposite(V,E);
if (getLabel(V2) == UNEXPLORED) {
setLabel(E, DISCOVERY);
DFS(G,V2);
} else {
setLabel(E, BACK);
}
}
}
}
Kälin Thomas, Abt I
29/35
06.07.2006
Programmieren 2
34.2. Spezialisierung
34.2.1.
Pfade finden
Wir können den DFS Algorithmus spezialisieren, um einen Pfad zwischen zwei gegebenen Knoten zu
finden. Dabei rufen wir den DFS-Algorithmus mit dem Startknoten auf, und merken uns den Pfad
zwischen dem Startknoten und dem aktuellen Knoten auf einem Stack. Sobald der Zielknoten
gefunden wurde, geben wir den Pfad mit Hilfe des Stacks aus.
34.2.2.
Zyklen finden
Wir können den DFS Algorithmus spezialisieren, um Zyklen in einem Graph zu finden. Wir merken uns
den Pfad zwischen dem Startknoten und dem aktuellen Knoten auf einem Stack. Sobald wir auf eine
BACK-Kante treffen, die auf den Startknoten zeigt, geben wir den Zyklus als Teil des Stacks aus.
34.3. Gerichtete Tiefensuche
Wie können den DFS-Algorithmus für gerichtete Graphen spezialisieren, indem Kanten nur in die
erlaubten Richtung traversiert werden. Dabei existieren 4 Verschiedene Kanten:
• Discovery (rot): Der aufspannende Baum bei der Tiefensuche
• Back (gestrichelt): Kanten von einem Nachfolger zu einem
Vorgänger.
• Forward (strich-punkte): Kanten von einem Vorgänger zu einem
seiner Nachfolger.
• Cross (gepunktet): Kanten zwischen zwei Teilästen des
aufspannenden Baums.
35.
Breitensuche (Breadth-First Search: BFS)
BFS ist eine generelle Technik für die Traversierung eines Graphen. Die Discovery-Kanten des BFS
bilden einen aufspannenden Baum vom G. BFS benötigt bei einem Graphen mit n Knoten und m
Kanten O(n+m) Zeit, sofern der Graph mithilfe seiner Adjazenzliste dargestellt wird.
35.1. BFS-Algorithmus
In einerInitialisierungsphase werden alle Knoten und Kanten mit dem Label „UNEXPLORED“
initialisiert. Danach läuft der folgende Algorithmus ab:
BFS(Graph G, Knoten s) {
L[0] = new List();
L[0].insertLast(s);
setLabel(s,VISITED);
int i = 0;
while (!L[i].isEmpty()) {
L[i+1] = new List();
foreach (Knoten v : L[i].elements()) {
foreach (Kante e : G.incidentEdges(v)) {
if (getLabel(e) == UNEXPLORED) {
Knoten w = G.opposite(v, e);
if (getLabel(w) == UNEXPLORED) {
setLabel(e,DISCOVERY);
setLabel(w,VISITED);
L[i+1].insertLast(w);
} else {
setLabel(e,CROSS);
}
}
}
}
i++;
}
}
Kälin Thomas, Abt I
30/35
06.07.2006
Programmieren 2
35.2. Applikationen
Mit Anwendung der BFS-Traversierung können wir folgende Probleme lösen:
ƒ Bestimmen der verbundenen Komponenten von G
ƒ Bestimmen eines aufspannenden Waldes von G
ƒ Bestimmen eines einfachen Zyklus in G
ƒ Findes eines Pfades in G zwischen den beiden Knoten mit einer minimalen Anzahl Kanten, oder
bestimmen, ob überhaupt so ein Pfad existiert.
36.
Gerichteter Graph / Digraph
Ein gerichteter Graph (directed graph, digraph) ist ein Graph, dessen Kannten ALLE gerichtet sind.
Anwendungen findet er bei Einbahnstrassen, Fluglinien und Task-Scheduling.
36.1. Transitiver Abschluss
Der transitive Abschluss stellt die gesamte Erreichbarkeitsinformation über
einen Digraph zur Verfügung. Der transitive Abschluss lässt sich berechnen,
indem eine Tiefensuche von jedem Vertex aus gestartet wird. Laufzeit ist
deshalb O(n*(n+m)).
In nebenstehenden Beispiel beschreiben die blauen Kanten den
ursprünglichen Graphen G, die roten Kanten ergänzen ihn zum transitiven
Abschluss G*.
36.2. Floyd-Warshall’s Algorithmus
Floyd-Warshall’s Algorithmus nummeriert die Knoten von G als v1…vn und berechnet eine Serie von
Digraphen G0…Gn, wobei Gn der transitive Abschluss darstellt. Die Laufzeit ist O(n3), sofern
areAdjacent() in O(1) läuft.
36.2.1.
Beispiel
In diesem Beispiel steht der Knoten V3 (rot) im
Mittelpunkt. Wie wir sehen, trifft beim Knoten V3
eine Kante von V6 ein. Ausserdem geht eine Kante
von V3 nach V4 weg. Da V6 und V4 noch nicht
direkt verbunden sind, erstellen wir eine neue
Kante.
Dies führen wir solange durch, bis alle
eingehenden mit allen ausgehenden Kanten
durchprobiert wurden. Danach gehen wir zum
nächsten Vertex (V4) und wiederholen das
Prozedere.
36.3. Topologische Sortierung
Dies ist eine spezialisierte Anwendung der Tiefensuche. Der Algorithmus ist identisch mit dem DFS,
mit dem Unterschied, dass nach der foreach-Schleife (also am tiefsten Punkt des aufspannenden
Baumes!) jeweils der Knoten noch mit einer Zahl beschriftet wird. Diese Zahl beginnt mit der Anzahl
an Knoten und wird dann bei jeder „Beschriftung“ um 1 dekrementiert. Wir nummerieren also
rückwärts.
Kälin Thomas, Abt I
31/35
06.07.2006
Programmieren 2
37.
Shortest Path / Kürzester Pfad
Gegeben sei ein gewichteter Graph mit zwei Knoten u und v. Wir möchten den kürzesten Weg
zwischen diesen beiden Knoten finden. Dies ist der Pfad, mit der geringsten Summe der Gewichte.
ƒ Eigenschaft 1: Ein Teilweg eines kürzesten Weges ist selbst auch ein kürzester Weg
ƒ Eigenschaft 2: Es existiert ein Baum von kürzesten Wegen, von einem Start-Vertex zu allen anderen
Vertices.
37.1. Dijkstra’s Algorithmus
Dieser Algorithmus berechnet die Distanzen zu allen Vertizes von einem Start-Vertex s aus und basiert
auf der Greedy-Methode. Wir gehen davon aus, dass der Graph verbunden, ungerichtet und ohne
negative Kantengewichte ist. Im ersten Schritt wird eine Wolke um den Startknoten gebildet.
Anschliessend wird in jeder Phase die lokal billigste Kante gesucht, welche „aus der Wolke hinaus“
führt. Diese wird dann in die Wolke mit einbezogen. Laufzeit ist O((n+m)*log(n)).
37.1.1.
Beispiel
37.1.2.
Probleme
Der Algorithmus funktioniert nicht für negative Kantengewichte. Wenn ein Vertex mit einer
Kantegewicht <0 später der Wolke hinzugefügt wird, bringt dies die Distanzen für die Knonten
durcheinander. Man könnte somit Zyklen erstellen, welche die Pfäde ungültig „verbessern“.
37.2. Bellman-Ford Algorithmus
Funktioniert auch mit negativen Kantengewichten, allerdings nur dann, wenn der Graph gerichtet ist.
Laufzeit ist O(n*m). Berechnet von jedem Knoten aus das Gewicht zum verbundenen Knoten (Start +
Kante = Gewicht). Ist dieses kleiner als dasjenige des Zielknotens, wird es überschrieben.
Kälin Thomas, Abt I
32/35
06.07.2006
Programmieren 2
37.3. DAG-basierter Algorithmus
Funktioniert wird der Bellmand-Ford Algorithmus auch mit negativen Kantengewichten, jedoch nur bei
gerichteten Graphen. Zusätzlich zum vorherigen Algorithmus werden die Knoten in topologischer
Reihenfolge angesprochen, was die Laufzeit auf O(n+m) verbessert.
38.
Minimum Spanning Tree
Ein MST ist ein aufspannender Baum eines gewichteten Graphen mit minimalem totalem
Kantengewicht.
38.1. Kruskal’s Algorithmus
Der Algorithmus bildet in der ersten Phase um jeden Vertex eine eigene Wolke. Anschliessend werden
alle Kanten in eine Priority Queue eingetragen. Als Schlüssel dient hierbei das Kantengewicht. Somit
wird bei jedem lesen der PQ die Kante mit dem minimalen Gewicht ausgelesen. Die Wolken um die
Endknoten der ausgelesen Kante werden miteinander verglichen. Sind diese unterschiedlich, so
werden Sie zu einer einzelnen Wolke vereinigt. Nun wird das nächste Element aus der PQ gelesen und
solange wiederholt, bis diese leer ist.
Kanten 2,3,7 bilden den MST.
Die Kante 6 wurde weggelassen,
da über Kanten 2 und 3 ein
billigerer Weg möglich ist.
Kälin Thomas, Abt I
33/35
06.07.2006
Programmieren 2
38.2. Prim-Jarnik’s Algorithmus
Dieser Algorithmus funktioniert ähnlich wie Djikstra’s Algorithmus. Es gibt jedoch einen kleinen
Unterschied: Hier wird jedes Mal der Knoten mit dem tiefsten Gewicht zur Wolke hinzugefügt. (Als
Erinnerung: Bei Djikstra war das Kriterium das Kantengewicht). Dies kann natürlich wieder optimal
mittels einer PQ programmiert werden, welche die Knoten ausserhalb der Wolke mit der Distanz als
Schlüssel beinhaltet. Laufzeit ist O(m*log(n)).
38.3. Baruvka’s Algorithmus
Ähnlich wie Kruskal’s Algorithmus, bildet dieser Algorithmus mehrere Wolken auf einmal. Das
Vorgehen ist hierbei wie folgt:
1) Wählen einen Knoten V aus
2) Suche alle mit V verbundenen Knoten. Für jeden dieser Knoten wird die Kante markiert, welche
das kleinste Gewicht aufweist. Ist diese Kante nicht in derselben Wolke wie V, wird die Kante
verbunden.
39.
XML: Document Type Definition (DTD)
Eine zu einem gegebenen XML-Dokument zugeordnete DTD beschreibt, welche Elemente und welche
Entitäten in diesem Dokument vorkommen müssen und dürfen. Gültigkeitsüberprüfung: Der Parser
prüft, ob die in der DTD definierten Anforderungen vom Dokument erfüllt sind. Ein Dokument kann
zwar wohlgeformt sein, aber dennoch ungültig (falls die DTD nicht eingehalten sind).
39.1. Beispiel (interne DTD)
<?xml version=“1.0“ encoding=“UTF-8“ ?>
<!DOCTYPE person [
//(#PCDATA) bedeutet nur geparste Zeichendaten, keine Child-Elemente
<!ELEMENT first_name (#PCDATA)>
<!ELEMENT last_name (#PCDATA)>
<!ELEMENT profession (#PCDATA)>
//Geordnete Folge von Child-Elementen, alle müssen vorkommen (Reihenfolge)
<!ELEMENT name (first_name, last_name)>
//Quantoren: ? -> (0-1)
* -> (0-n)
+ -> (1-n)
<!ELEMENT person (name, profession*)>
]>
<person>
<name>
<first_name>Thomas</first_name>
<last_name>Kaelin</last_name>
</name>
<profession>drug dealer</profession>
Kälin Thomas, Abt I
34/35
06.07.2006
Programmieren 2
<profession>information technologist</profession>
</person>
39.2. Beispiel (externe DTD)
<?xml version=“1.0“ encoding“UTF-8“ standalone=“no“ ?>
<!DOCTYPE person SYSTEM „external.dtd“ >
39.3. Spezielles
//Kreditkarte oder Bar-Child möglich
<!ELEMENT Zahlungsbetrag (Kreditkarte|Bar)>
<Zahlungsbetrag><Kreditkarte>Visa</Kreditkarte></Zahlungsbetrag>
//Leere Elemente werden mit EMPTY deklariert
<!ELEMENT BR EMPTY>
<BR></BR>
//Beliebige Elemente können Child-Elemente sein: ANY
<!ELEMENT Weltall ANY>
<Weltall>Unendlich</Weltall>
<Weltall><Planet>Erde</Planet></Weltall>
Kälin Thomas, Abt I
35/35
06.07.2006
Herunterladen