Aud – Zusammenfassung
-
Man muss sich immer die Implementierte Beispiele ansehen und zumindest verstehen 
§2 Listen
Listen als Felder
-
-
-
Attribute
o T[] data
speichert Daten als Feld
o Int capacity
Länge des Feldes = momentan verfügbarer Speicherplatz
o Int size
Anzahl Einträge in Vector = momentan benötigter Speicherplatz
Operationen
o T at(int i)
Zugriff auf Eintrag i
o Reserve (int i)
reserviert Einträge/ erhöht capacity
o Rezise (int n)
erweitert oder schrumpf Vector
o Push_back(T obj)
fügt Eintrag am Ende ein
o Insert_back(int i, T obj)
fügt Eintrag an Position i ein
o Pop_back()
löscht den letzten Eintrag
o Erase()
löscht Eintrag i
Mittlerer Aufwand n:size() Einträge
o Size
O(1)
o At
O(1)
o Push_back
O(1)
o insert
O(n)
o pop_back
O(1)
o erase
O(n)
o Verschieben O(n)
Verkettete Listen
-
Jetzt Referenzen auf den folgenden Eintrag
Jeder Eintrag wird als Knoten (node) erfasst
(data, next) -> null
Operationen wie oben
Mittlerer Aufwand n:size() Einträge (immer O(n)
Einfügen/ Löschen jetzt in O(1) falls Position bekannt (front, push_front, pop_front)
Einfügen / Löschen = Umsetzen der next Referenz
Doppelt verkettete Listen
-
-
Referenz auf Vorgänger, Nachfolger (vorne und hinten = null bzw. tail oder head)
Mittlerer Aufwand für n:size() Einträge
o Size
O(n)
o Front/back
O(1)
o At
O(n)
o Push_front/push_back
O(1)
o Insert
O(n)
o Pop_front/ pop_back
O(1)
o Erase
O(n)
Einfügen/Löschen = Umsetzen der prev und next Referenzen
Iteratoren (Abstraktes Konzept zum Traversieren von Daten (Durchlaufen der Liste von head nach tail))
S. 1
Stack
-
Top
Push
Pop
Is_empty
oberstes Element auslesen
Element auf den Stapel ablegen
oberstes Element von Stapel nehmen
Ist Stapel leer ?
Enqueue
Dequeue
Front
Element hinten anstellen
Element vorne entfernen
vorderstes Element auslesen
Queue
-
(Ringpuffer, DList)
§3 Bäume
-
Knoten(node), Wurzel(root), Kante(edge)
Kanten verbinden Knoten
Bäume wachsen von oben nach unten, Wurzel = oberster Knoten
Kinderknoten(child), Elternknoten(parent), Teilbaum (subtree), Blatt(leaf)
Wurzel hat kein Elternteil
Jeder andere Knoten hat genau ein Elternknoten
Jeder Knoten definiert einen Teilbaum.
Interne und externe Knoten, Blätter haben keine Kinder
Niveau 0, Niveau 1,...
Pfad = Folge von durch Kanten verbundene Knoten
Zu jedem Knoten existiert genau ein Pfad von der Wurzel
Höhe = maximales Niveau + 1 bzw. Anzahl Ebenen
-
Ein Baum, bei dem jeder Knoten höchstens zwei Kinderknoten hat, heißt Binärbaum.
Ein Binärbaum heißt voll, wenn jeder Knoten entweder 0 (Blätter) oder 2 (innere Knoten) Kinder hat.
Ein voller Binärbaum heißt vollständig, wenn alle Blätter auf derselben Ebene liegen.
Binärbäume in Java
-
-
Attribute
o BinaryTree<T> parent_
o BinaryTree<T> left_, right_
o T data_
Zugriff über Selektoren
o BinaryTree<T> getParent()
o BinaryTree<T> getLeft(), getRight()
o Boolean isRoot()
o Boolean isLeaf()
Vater
Kinder
speichert Datum
ersetzt Eltern
ersetzt Kinder
traversiert aufwärts und liefert Wurzel
Traversierung von Binärbaumen
-
S. 2
Rechenaufgaben mit Stack bzw. Binärbaum (MUSS MAN VERSTANDEN HABEN !!!)
Durchlauf des gesamten Baumes
o Infix-Notation
x+y
o Postfix- Notation
xy+
o Prefix – Notation
+xy
Preorder Traversal
o Aktueller Knoten, Linker Teilbaum, Rechter Teilbaum
-
-
Inorder Traversal
o Linker Teilbaum, Aktueller Knoten, Rechter Teilbaum
Postorder Traversal
o Linker Teilbaum, Rechter Teilbaum, Aktueller Knoten
Levelorder Traversal
o Durchlauf der einzelnen Ebenen von links nach rechts
(Syntaxanalyse, BNF, ...)
Endrekursion
o Rekursiver Aufruf ist letzte Aktion (LERNEN WIE DAS GEHT !!!)
§4 Suchbäume
Binäre Suchbäume
-
-
-
-
S. 3
Knoten X enthält Schlüsselwert key().
Baumstruktur definierter Ordnung: Alle Schlüsselwerte im
o Linker Teilbaum left() sind kleiner als key()
o Rechter Teilbaum right() sind größer als key()
Suche (Beginn bei Wurzel, Abstieg in Baum, Rekursiv oder Iterativ)
Minimum = Knoten „links unten“ // Maximum = Knoten „rechts unten“
Inorder – Iteration zählt sortierte Folge auf
Bereich = range(), sortierte Folge im Intervall (Teilfolge)
Neuen Eintrag / Knoten einfügen
o Erfolglose Suche -> letzter besuchter Knoten X
o Erzeuge neunen Knoten Y mit Schlüssel k
o Einhängen von Y unter X als linkes oder rechtes Kind
Hilfsknoten (Head, nil)
Löschen eines Eintrages (Hochziehen eines Teilbaumes)
o X ist Blatt, X hat kein Kind
 Entferne Knoten X
o X hat ein Kind
 Ersetzte Knoten X durch dessen Kind Y (=Teilbaum)
 Hochziehen von Y
 Hier analog zum Löschen aus einer verketteten Liste
o X hat zwei Kinder
 Suche Knoten M in R, der am weitesten links steht (Minimum)
 Seien P Vater und MR rechtes Kind von M
 Ersetze X durch M
 Setze L als linkes Kind und R als rechtes Kind von M
 Setze MR als linkes Kind von P ein
Höhe abhängig von Einfügereihenfolge
Mögliche Binärbäume (Übersicht kennen !!)
Rotationen
o Einfach Rotation (rechts // links)
o Doppel Rotation (rechts- links // links – rechts)
o Algorithmus
 Sei a,b,c Inorder-Folge der Knoten x,y,z. Seien T0, T1, T2, T3 Inorder-Folge der Teilbäume
unter x,y,z.
 Ersetze Teilbaum mit Wurzel z durch Teilbaum mit Wurzel b
 Nimm a als linkes Kind von b mit Kindern T0, T1.
 Nimm c als rechtes Kind von b mit Kindern T2, T3.
Balancierte Bäume
-
Balance/Ausgleich = minimiere Höhe,
Keine Entartung zur Liste, aber auch keine voll ausgeglichenen binär Bäume möglich.
Stattdessen Garantien (Höhe h begrenzt durch O(logn))
AVL –Bäume
-
-
Definiere Balance als Höhenunterschied bal(T) = h(TR) – h(TL)
Ein binärer Baum heißt AVL-Baum, wenn bal(T) € {-1,0,1} an allen Knoten (für alle Teilbäume) T gilt.
o Das heißt die Höhe von linkem und rechten Teilbaum unterscheiden sich höchstens um 1.
o AVL-Kriterium muss für alle Teilbäume ausschlusslos gelten.
Einfügen in AVL-Baum
o Ist ja Binärer Suchbaum
o Einfügen von neuem Knoten
o Wenn AVL-Eigenschaft verletzt wiederherstellen der Eigenschaft
 Rotation oder Doppelrotation (2 Varianten: LL; RL)
2-3-4 Bäume
-
-
-
Jeder interne Knoten hat 2,3 oder 4 Kinder.
Jeder innere Knoten hält 1,2 oder 3 Schlüssel.
Sortierte Reihenfolge der Schlüssel (Teilbäume)
Split
o Reihenfolge der Teilbäume bleibt erhalten
o Neuer Wurzelknoten b wird in Elternknoten hochgezogen.
 Verschmelzung (merge) mit Elternknoten
Bottom-Up
o Erfolglose Suche bis zu einem Blatt
o 1.Fall Einfügen in 2- oder 3- Knoten
o 2.Fall Einfügen in 4-Knoten
 Teile 4-Knoten (split)
 Split des Elternknoten möglich, ggf. bis zu Wurzel möglich
o Splits von „unten nach oben“
Top-Down
o Erfolglose Suche bis zu einem Blatt
o Teile dabei im Abstieg alle besuchten 4-Knoten (split)
o Füge Eintrag in Blattknoten ein
o Splits von „oben nach unten“
Rot-Schwarz-Baum
-
-
S. 4
Ein Rot-Schwarz-Baum ist ein binärer Suchbaum, in dem jedem Knoten ein Farbattribut zugeordnet ist, so
dass gilt:
o Ein Knoten ist entweder rot oder schwarz
o Die Wurzel ist schwarz
o Alle Blätter sind schwarz
o Beide Kinder eines roten Knotens sind schwarz
o Jeder Pfad von einem gegebenen Knoten zu einem erreichbaren Blatt enthält dieselbe Anzahl
schwarzer Knoten
o (Blätter sind Null-Knoten)
Umstrukturierung bei Verletzung der Eigenschaften für Balance (BILDER ANSCHAUEN !!!!)
Eigenschaften von AVL- und Rot-Schwarz-Bäumen
-
Baum mit n Einträgen benötigt Speicherplatz in O(n)
Suche in O(logn)
Einfügen O(logn)
Löschen O(logn)
Suchen in AVL-Bäumen ist effizienter, da geringere Höhe
Einfügen in Rot-Schwarz-Baum ist effizienter, möglich.
B-Bäume
-
-
-
AVL-und Rot-Schwarz-Bäume = viele kleine Datenpakete geschrieben
Für zB. Dateisysteme (Verzeichnisstruktur) oder Datenbanken sind balancierte Binärbäume nicht geeignet.
Annahmen:
o Alle Pfade zu den Blättern sind gleich lang.
Ein Baum heißt B-Baum, wenn er für m > 0 folgende Eigenschaften erfüllt:
o Jeder Knoten hat höchstens 2m+1 Kinder
o Jeder Knoten hat mindestens m+1 Kinder – mit Ausnahme der Wurzel: sie hat mindestens 2 Kinder
oder ist ein Blatt
o Jeder innere Knoten mit k Schlüsseln hat k+1 Kinder
o Alle Blätter liegen auf der gleichen Ebene.
Jeder Knoten ist mindestens bis zu Hälfte gefüllt.
Es wird immer eine ganze Seite gelesen / geschrieben.
Suche
o Beginne bei Wurzel
o Suche Schlüssel k im Knoten
o Ggf. rekursiver Abstieg.
Einfügen
o Erfolglose Suche bis zu einem Blattknoten
o 1. Fall Knoten hat <2m Einträge -> in Liste einsortieren
o 2.Fall Knoten hat =2m Einträge -> erzeuge neuen Knoten
 Linke Hälfte der Einträge (m Stück) bleibt
 Rechte Hälfte wird in neuen Knoten verschoben
 Mittlerer Eintrag wird in Elternknoten eingefügt
o Einfügen setzt sich ggf. rekursiv bis zur Wurzel fort.
(SPlay Tree, Tries, Set, Map)
§5 Heaps
-
-
S. 5
Priority queue (Erledigt wichtigste Aufgabe zuerst, sortiert sich selbst, nach Kriterium (festgelegt))
Ein Binärbaum ist ein Heap, wenn für jeden Knoten X (außer der Wurzel) gilt X>= parent(X).
o Min-Heap
o Zugriff auf kleinsten Eintrag (=Wurzel) in O(1)
o Alle Ebenen bis auf letzte sind vollständig gefüllt.
o Die letzte Ebene ist „linksbündig“ gefüllt.
o Baum lässt sich ebenenweise auf ein Feld ak abbilden.
Operationen
o Zugriff auf den kleinsten Eintrag
o Einfügen eines neuen Eintrags
o Entfernen des kleinsten Eintrags (Wurzel)
-
-
-
-
-
-
o Ersetzen des kleinsten Eintrags
o Verwende Einfügen und Entfernen zum Sortieren: Heapsort
Im folgenden sei a Heap mit n Einträgen ak, 1<=k <=n
Einfügen
o Einfügen als neuen Blattknoten
o Erhalte dabei Struktureigeenschaft „linksbündig“
o Heapeigenschaften wieder herstellen
 Beginne mit neuem Knoten x
 Solange Wert des Vaterknotens p >x
 Vertausche Werte von p und x
 Weiter mit Überprüfung von Vaterknoten p
o Eingefügter Knoten wandert solange nach oben, bis die Heap-Eigenschaft wieder gilt.
Maximaler Aufwand ist O(logn), da Binärbaum balanciert ist.
Entfernen des kleinsten Eintrags
o Lösche Wurzel
o Ersetze Wurzel durch Eintrag „rechts unten“
o Heap schrumpft um letzten Eintrag
o Heap – Eigenschaft wieder herstellen
 Beginne mit neuer Wurzel
 Solange Wert von w größer als kleinster Wert der Kinder
 Vertausche Werte Wurzel <-> Kind
 Weiter mit entsprechendem Kind als neuer Wurzel w
o Knoten wandert nach unten, bis Heap-Eigenschaft wieder gilt
o Maximaler Aufwand ist O(log n), da Binärbaum balanciert ist.
o Balance bleibt wieder erhalten !
Bottom-up
o Annahme: alle Teilbäume unter Ebene i sind bereits Heaps
o Kann Heaps auf Ebene i durch downheap konstruieren
o Starte mit vorletzter Ebene i = h-1 -> Annahme erfüllt
 Konstruiere alle Heaps bis Ebene i mit downheap
 Wiederhole für i:=i-1 bis Wurzel erreicht wird
o Verschmelzen von Heaps mittels dwonheap
o Rechenbeispiel (schlechtester Aufwand = O(n))
Top-down
o O(n log n) im schlechtester Aufwand
o Erzeuge Binärbaum durch insert
Eigenschaften von Heapsort
o Heapsort benötigt O(n log n) Operationen im Mittel und im schlechtesten Fall
o Heapsort arbeitet in-place
o Heapsort ist nicht stabil !!!
o Sortierreihenfolge beachten !!!
Aufwand im schlechtesten Fall
o Minimum / Maximum
O(1)
o Einfügen
O(log n)
o Entfernen
O(log n)
o Kleinstes Ersetzen
O(log n)
o Aufbau/Zusammenführen
O(n)
o Heapsort
O(n log n)
§6 Hashverfahren
S. 6
Bijektive Funktion zur Zuordnung
Adressierung durch Schlüssel, aber begrenzter Speicher, und Suche in konstanter Zeit
-
-
Prinzip
o Einfache Datenstruktur (Feld, Tabelle)
o Hashtabelle (Streuwerttabelle)
o Bestimmung der Position eines Objektes durch Hashfunktion
 Objekt als Paar von Schlüssel und Wert
 Adressierung: Anwendung der Hashfunktion auf Schlüssel
Eigenschaften
o Eine Hashfunktion muss
 Konsistent sein
 Deterministisch sein
o Eine Hashfunktion soll möglichst
 Effizient berechenbar sein (in jedem Fall O(1))
 Zu einer Gleichverteilung von Schlüsseln führen
 Möglichst wenige Kollisionen
 Ganze Tabelle ausnutzen
o Die Hashfunktion soll Daten möglichst gut streuen, d.h. möglichst zufälliges Verhalten.
o H(x)=x mod m (Streuung hängt von m ab)
o Überlauf beachten
Kollisionen
-
-
S. 7
Verkettung innerhalb eines bucket
o Jeder Tabelleneintrag speichert Liste von Einträgen (verkettete Liste)
o Einfügen
 Neuer Eintrag wird in Liste eingefügt
 Bei Kollision hat Liste schon mehr als einen Eintrag
o Suchen
 Abbruch bei leerer Liste -> erfolglose Suche
 Ansonsten wird Liste nach Eintrag durchsucht, dazu wird jeder Eintrag auf Gleichheit
getestet
 Diese sequenzielle Suche kann immer noch erfolglos sein !!
o Löschen
 Erfolgreiche Suche liefert Liste und Listeneintrag
 Lösche Eintrag aus Liste
o Eigenschaften
 Einfach zu implementieren
 Einfügen in konstanter Zeit O(1)
 Reduziert Anzahl Vergleiche bei Suche im Mittel um Faktor m
 Im Vergleich zur Liste
 Trotzdem im schlechtesten Fall ebenso O(n)
 Keine Sonderbehandlung von gelöschten Einträgen
 Durchschnittlicher Aufwand für Suche (a = Durchschnittslänge von bucket)
 Erfolglos 1+a
 Erfolgreich 1+ (a/2)
Offene Adressierung
o Lineares Sondieren
 Falls ein Eintrag h(x) bereits besetzt ist, teste nacheinander die Einträge, solange bis ein
freier Eintrag gefunden wird.
 Sondieren beim Einfügen und Suchen.
 Achtung beim Löschen
 Ersetzen durch speziellen Wert, der eine vormals belegte Position markiert.
 Sonst wären nachfolgende Einträge nicht mehr erreichbar !
-
Klumpenbildung ist hier Gefahr (schelchte Streuung, schlechte Ausnutzung)
Anzahl Sondierungen im Mittel
 Erfolglos (1/2)(1+(1-a)-²)
 Erfolgreich (1/2) (1+(1-a)-1 )
 Einfügen und Suchen mit linearen Sondieren benötigt im Mittel weniger als 5 Sondierungen
 Je höher Füllstand, desto schlechter Such- und Einfügeverhalten
 Einfügen und Suchen im Mittel in O(1)
 Aber Aufwand für rehashing in O(n) !!
o Quadratisches Sondieren
 Wie lineares Sondieren, aber quadratische Funktion
 Ziel: Clustering vermeiden
 Falls Eintrag h(x) bereits besetzt ist, teste nacheinander die Einträge, solange bis ein freier
Eintrag gefunden wird.
 Allgemeiner: addiere quadratische Funktion
 Anfällig für schlecht gewählte Tabellengröße m !
 Immer noch „Sekundärkluster“ (Abbildung von Mustern)
o Doppel-Hashing
 Ziel: Reduziere clustering effektiv !
 Verwende zweite Hashfunktion h2 ungleich h
 Falls Eintrag h(x) bereits besetzt ist, teste nacheinander die Einträge h(x)+i*h2(x)
 Anzahl Sondierungen im Mittel
 Erfolglos (1/(1-a))
 Erfolgreich (1/a) ln (1/(1-a))
 Doppel-Hashing benötigt im Mittel weniger Sondierungen las lineares Sondieren
Wichtige Übersicht
o Typischerweise Suchen, Einfügen, Löschen in O(1)
o Abhängig von Füllgrad a
o Einfügen im schlechtesten Fall in O(n)
(Dynamische Hashverfahren, Anwendungen von Hashfunktionen,...)
§7 Graphen
-
-
-
S. 8
Ungerichtete Graphen
o Ein ungerichteter Graph ist ein geordnetes Paar G = (V,E) mit
 einer Menge von Knoten v und
 einer Menge von Kanten E
o Kanten sind ungerichtet (beide Richtungen)
Gerichtete Graphen
o Ein gerichteter Graph ist ein geordnetes Paar G = (V,E) mit
 einer Menge von Knoten V
 einer Menge von Kanten E
o Kanten sind jetzt geordnetes Paar, d.h. gerichtet
o Schleifen sind erlaubt
Gewichtete Graphen
o Ein gewichteter Graph ist ein (gerichteter oder ungerichteter) Graph G=(V,E) zusammen mit einer
Funktion.
o Gerichtet oder ungerichtet möglich
o Andere Wertebereiche möglich
o Keine negativen Zahlen bzw. Werte
Begriffe zu Graphen
-
-
-
-
Teilgraph definiert durch Teilmenge von V und E
Grad eines Knotens
o Anzahl der eingehenden Kanten in ungerichteten Graphen
o Unterscheide indegree und outdegree in gerichteten Graphen
Ein gerichteter Graph ist symmetrisch, wenn zu jeder Kante (i,j), auch die entgegengesetzte Kante
(j,i)existiert.
Eine Folge von Knoten v1,v2,vn ... die durch Kanten verbunden sind heißt
o Pfad (wenn alle Knoten vi verschieden sind)
o Zyklus
Ein gerichteter Graph heißt zyklisch, wenn er einen Zyklus enthält.
o Ansonsten azyklisch
Multigraphen
o Verbindung von zwei Knoten durch mehr als eine Kante
o Als Abgrenzung oft: einfacher Graph = ungerichteter Graph ohne Mehrfachkanten
Hypergraphen
o Kante verbindet mehr als einen Knoten gleichzeitig
o Kante = Teilmengen verbundener Knoten
Datenstrukturen für Graphen (ANSEHEN !!!)
-
-
-
-
S. 9
Kantenliste
o Speichere Kanten E als Liste
o Erste zwei Einträge bezeichnen die Anzahl Knoten und Kanten.
o Je zwei aufeinanderfolgende Einträge bezeichnen eine Kante.
o Knoten werden durch Indices (int) bezeichnet.
Knotenliste
o Speichere Nachbarschaft von Knoten für gerichteten Graphen ausgehende oder eingehende Kanten.
o Erste zwei Einträge bezeichnen die Anzahl der Knoten und Kanten.
o Nächste Zahl speichert Anzahl ausgehender Kanten.
o Er folgt Liste der Nachbarknoten.
o Auch Möglichkeit in zwei Listen zu speichern:
 Neue Tabelle nodeNhd speichert Indices in neighborhoods, Längenangaben entfallen.
 Nachbarschaft des i-ten Knotens in neighborhoods, an Position nodeNhd[i-1]
 Anzahl Knoten = nodeNhd.length-1
 Anzahl Kanten = nodeNhd[nodeNhd.length-1]
Eigenschaften von Kanten- und Knotenlisten
o Eignen sich für nicht veränderbare Graphen
o Kantenlisten
 Einfaches Zeichnen
 Aufzählung der Nachbarschaft von Knoten
 Knotenindices werden mehrfach gespeichert
o Knotenlisten
 Erlaubt einfache Aufzählung der Nachbarschaft von Knoten
 Zusätzliche Tabelle von Knoten erleichtert Suche !
 Entscheidung (i,j) benötigt Suche innerhalb Nachbarschaft
 Effiziente Aufzählung der eingehenden Kanten erfordert zweite Tabelle (für gerichtete
Graphen).
Adjazensmatrizen (ANSEHEN !!!!)
o Kodiere Kanten als Einträge in Matrix.
o Zeile i = Index des Startknotens
o
o
o
o
o
-
Spalte j = Index des Endknotens
Menge X mit 0 (X= {0,1}, X=N)
Zeile i in A liefert Nachbarschaft von Knoten i (von i ausgehende Kanten)
Spalte j in A liefert in j eingehende Kanten
Adjazensmatrizen sind symmetrisch für
 Ungerichtete Graphen
 Symmetrische (gerichtete) Graphen
o Es genügt, untere oder obere Dreiecksmatrix zu speichern.
o Ohne Diagonale, falls keine Schleifen erlaubt sind.
o Gewichteter Graph -> Zahlen codieren Gewichte. (Gewicht 0 ist unzulässig.)
o Eigenschaften:
 Einfügen / Löschen von Kanten in O(1)
 Entscheiden (i,j) in O(1)
 Aufzählen der Nachbarschaft eines Knoten in O(n) (Auslesen einer Zeile)
 Einfügen eines Knotens O(n²)
 Erweitern auf (n+1)*(n+1) Matrix
 Speicher vorreservieren
 Löschen eines Knotens
 O(n) falls Einträge in Zeile und Spalte auf 0 gesetzt werden
 O(n²) falls Zeile und (!) Spalte gelöscht werden
 Speicherbedarf in O(n²)
 Alle Operationen sind abhängig von Anzahl der Kanten
Adjazenslisten
o Speicherbedarf O(|V|+|E|)
o Aufwand für Operationen abhängig von Listendarstellung
o Einfügen von Kanten
 Muss testen ob Kante bereits existiert !
 Erfordert Suche in Nachbarschaft
o Löschen von Knoten
 Muss Knoten auch aus Nachbarschaften löschen !
 Erfordert Suche in alle Nachbarschaften !
(Übersicht des Aufwandes ist in Tabelle §7 Folie 31 sichtbar dargestellt)
(Dünnbesetzte Adjazensmatrizen, Implementierung, Beweis,...)
§8 Algorithmen auf Graphen
-
Tiefensuche (DFS)
o Vorgehensweise als Quelltext
Breitensuche (BFS)
o Vorgehensweise als Quelltext
Bemerkungen DFS / BFS
o Liefert Baum mit Startknoten als Wurzel = aufgespannter Baum des Graphen (spanning tree)
(Zeitpunkte, Distanzen, Klassifizierung,...)
-
S. 10
Topologisches Sortieren
o Gegeben ist ein azyklischer gerichteter Graph
o Gerichtete Kanten definieren eine partielle Ordnung
o (i,j) -> i kommt vor j
o Interpretiere Knoten
o Kanten modellieren Abhängigkeiten
o
o
o
o
-
-
-
-
-
S. 11
Gesucht ist eine Reihenfolge der Knoten, so dass jeder Knoten nach all seinen Vorgängern erscheint
Reihenfolge i.a. nicht eindeutig !!!
Reihenfolge: Quellen am Anfang, Senken am Ende
Idee:
 Starte von allen Senken
 DFS in umgekehrter Richtung (entlang eingehender Kanten)
 Gibt Knoten aus, wenn alle eingehenden Kanten abgearbeitet
o Zu ausgegebenen Knoten gibt es keine Abhängigkeit mehr.
o Erweiterung zum Erkennen von Zyklen
 Laufe über Kante (i,j) (rückwärts j -> i), wobei i noch nicht ausgegeben aber schon markiert
o Idee 2
 Finde alle Knoten ohne eingehende Kanten (Quellen)
 Gibt diese Knoten aus und...
 entferne sie zusammen mit allen ausgehenden Kanten
 Wiederhole bis Graph leer ist.
 Zyklus falls Graph nicht leer und keine Quelle gefunden wird.
 Graph wird verändert statt durchlaufen.
Kürzeste Wege
o Finde kürzeste Wege in gewichteten Graphen.
o Länge von Wegen = Summe der Kantengewichte
o Idee:
 Modifizierte Breitensuche
 Besuche immer den nächstgelegenen Knoten
Priority Queue (Quellcode)
Priority fist search = Algorithmus von Dijkstra
o Beachte mögliche Aktualisierungen von Distanzen
 D[t] wird erniedrigt, Elternknoten p[t] wird angepasst,
 Aktualisieren der PQ durch open_lower(t)
o Berechnen der kürzesten Distanzen d[] zu allen Knoten und des shortest path tree (SPT)
o Pfade in SPT zur Wurzel = kürzeste Wege zum Start
o Alternativ:
 Kürzester Weg = kürzeste Wege zum Start
 Erster Knoten = Startknoten
 Abbruch, sobald der zweite Knoten abgearbeitet wurde
Greedy Algorithmus
o Wähle in jedem Schritt die aktuell günstigste Möglichkeit !
o Entscheidung anhand lokaler Kriterien
o Gefahr: Erreichen und Verharren in lokalen Minimum
o Für Priority First Search
 Nimm Knoten mit kürzester Entfernung aus PQ
 Revidiere ggf. vorhergehende Berechnung
 PFS findet global kürzeste Wege
Backtracking (LESEN !!!)
Minimum Spanning Tree (MST)
o Als Spannbaum oder spanning tree ST) eines ungerichteten (und zusammenhängenden) Graphen
bezeichnet man einen Teilgraphen, der (1) ein Baum ist und (2) alle Knoten des Graphen enthält.
o Ein Spannbaum eines gewichteten Graphen heißt minimaler Spannbaum oder minimum spanning
tree (MST), wenn die Summe der Kantengewichte minimal ist. D.h. es gibt keinen weiteren
Spannbaum mit geringerem Gesamtgewicht.
Kürzeste Wege und MST berechnen mit PFS
o Algorithmus von Dijkstra zur Berechnung kürzester Wege
 Wähle Knoten mit kürzester Distanz
o
 Priorität = Wegstrecke
 Minimiere Länge von Pfaden im Spannbaum -> SPT
Algorithmus von Prim zur Berechnung des MST
 Wähle Knoten, der über kürzesten Weg erreichbar ist
 Priorität = Kantenlänge
 Minimiere Summe der Kantengewichte im Spannbaum -> MST
(Bellmann Ford Alg. , A* Alg.,....) //Fluss in Netzwerken // -> nicht relevant
§9 Dynamische Programmierung
-
-
-
Optimierungsprobleme (höchster Gewinn, geringste Kosten)
Systematisches Durchsuchen des Lösungsraumes
Rucksackproblem (VERSTEHEN !!!!)
Zielfunktion, Nebenbedingungen
Brute-Force-Suche
o Systematisches Aufzählen und Bewerten aller Belegungen
o O(2^n)
o Frühzeitiger Abbruch möglich, falls Kapazität erschöpft !
o Verbesserung durch pruning (Stutzen von unzulässigen Zweigen im Entscheidungsbaum)
Greedy-Algorithmus
o Bei jeder Entscheidung siegt die Gier
o Reduktion auf wenige Entscheidungen
o Beschneidung des Lösungsraums
o Keine Garantie, dass optimale Konfiguration erreicht wird !
o Lösung abhängig von Entscheidungskriterium
o Es ist oft möglich gute Konfigurationen schnell zu finden.
o In der Regel wird nur das lokale Optimum erreicht!
Optimale Lösung besteht aus optimalen Teillösungen
Gilt rekursiv für jede optimale Teillösung
Es müssen nur optimale Teillösungen beachtet werden.
Speicherung der Zwischenergebnisse in Tabelle, damit man sie nicht immer neu berechnen muss.
Backtracking zum Finden der optimalen Lösung notwendig (ANSEHEN !!!)
Dynamische Programmierung
o Ziel: Lösen von Optimierungsproblemen
o Zerlege Problem in einfache Teilprobleme
 Kleine Probleme können effizient gelöst werden
 Am besten Definition mit Indizes
o Annahme:
 Optimale Lösung, besteht aus optimalen Teillösungen
o Optimale Teillösungen sollten sich überlappen, d.h. voneinander unabhängige Teile können
wiederum gemeinsame Teilprobleme enthalten
 Im Gegensatz zu divide and conquer Ansatz
 Jede Teillösung wird einmal berechnet und gespeichert
o Definition der optimalen Lösung i.d.R rekursiv
o Iterativer Algorithmus zur Lösung
 Von kleinen zu größeren, zusammengesetzte Teilprobleme
 Speichere Lösungen in Tabelle
Voraussetzungen für Dynamische Programmierung
S. 12
Problem lässt sich geeignet zerlegen
Optimalitätsprinzip muss gelten
Beispiele für solche Probleme (siehe Hefter 9/30)
Optimierung mit Dynamischer Programmierung
-
-
Analysiere Problem
o Charakterisierung der Lösung mit ganzzahligen Indizes ?
o Optimalitätsprinzip gegeben ?
o Überlappende optimale Teillösungen ?
Definiere optimale Lösung durch Rekursionsgleichung
Iterative Konstruktion der Lösung
o Speichere optimale Teillösungen in Tabelle (Indizierung nötig !!)
o Bottom – up Konstruktion
Ansätze zur Lösung von Problemen
-
Fundamentale Prinzipien zum Entwurf von Algorithmen
Schrittweise Verfeinerung
Teile und Herrsche (divide and conquer)
Greedy Methoden
Dynamische Programmierung
Backtracking als Baustein
(Matrixprodukte, Caveat,....)
S. 13