- 13.04.2018: Die Einsicht der Nachklausur findet statt
am Dienstag, den 24.04.2018, von 16–17 Uhr im SR 006.
Dabei kann auch die erste Klausur noch einmal eingesehen werden.
- 13.04.2018: Die Nachklausur ist korrigiert. Sie können
Ihre Punkte im Online-KVV
einsehen. Die Benotung erfolgt nach diesem
Schema. Der Termin für die Einsicht
wird noch bekannt gegeben.
- 12.04.2018:
Am Donnerstag, den 12.04.2018, findet von 10–12 Uhr
die Nachklausur statt. Außer Schreibutensilien ist lediglich
ein beidseitig handbeschriebenes DIN-A4 Blatt als Hilfsmittel erlaubt.
Bitte bringen Sie ein Personaldokument mit Lichtbild sowie Ihren Studierendenausweis mit.
Die Klausur findet statt im Hörsaal 2 in der Habelschwerdter
Allee 45. Bitte bis 10:10 Uhr die Plätze einnehmen.
Die Klausur beginnt um 10:15 Uhr.
- 26.02.2018:
Es wird ein Zusatztutorium für die Vorbereitung auf die
Nachklausur geben, angeboten von Katharina Klost und
Benjamin Berendsohn.
Dieses findet statt am Freitag, den 06.04.2018 von
14:00–16:00 Uhr, im großen Hörsaal in der
Takustr. 9.
Wünsche für konkrete Aufgaben bzw. Themen sollten vorab per
Mail abgegeben werden.
- 26.02.2018:
Die Änderungen aus der Klausureinsicht sind nun im
KVV
eingetragen. Bitte kontrollieren Sie Ihren Eintrag und melden Sie
sich, falls es noch Fragen gibt.
- 23.02.2018: Die Klausur ist korrigiert. Sie können
Ihre Punkte im Online-KVV
einsehen. Die Benotung erfolgt nach diesem
Schema. Die Klausureinsicht findet
statt am Montag, den 26.03.2018, von 13:00–14:00 Uhr im SR 049.
Die Nachklausur findet statt am Donnerstag, den 12. April 2018, von
10–12 Uhr, im Hörsaal 2 der Silberlaube.
- 19.02.2018: Am Dienstag, den 20.02.2018 findet
von 14–16 Uhr die Klausur statt. Außer
Schreibutensilien ist lediglich ein beidseitig handbeschriebenes
DIN-A4 Blatt als Hilfsmittel erlaubt. Bitte bringen Sie ein
Personaldokument mit Lichtbild sowie Ihren Studierendenausweis mit.
Die Klausur findet statt im Henry-Ford-Bau, Garystr. 35. Nachnamen
A–G schreiben im Hörsaal A, Nachnamen H–L im
Hörsaal B und Nachnamen M–Z im Hörsaal C. Bitte
bis 14:10 Uhr die Plätze einnehmen. Die Klausur beginnt um 14:15 Uhr.
- 12.02.2018: Die Tutoriumsaufgaben sind im Online-KVV
verfügbar.
- 12.02.2018: Aufgrund des Warnstreiks der Tutoren wird
das vierzehnte Aufgabenblatt nicht korrigiert.
- 02.02.2018:
Am Dienstag, den 13. Februar, findet die Vorlesung im
Hörsaal 001 der Mathematik, Arnimallee 3, statt.
- 02.02.2018:
Die Klausuranmeldung ist freigeschaltet.
Falls Sie die Klausur mitschreiben möchten, melden Sie
sich bitte bis zum 18. Februrar 2018 im KVV an.
- 29.01.2018: Das vierzehnte Aufgabenblatt
ist verfügbar. Dies ist das letzte Aufgabenblatt.
- 22.01.2018: Das dreizehnte Aufgabenblatt
ist verfügbar. Dies ist das vorletzte Aufgabenblatt.
- 22.01.2018: Aufgrund des Warnstreiks der Tutoren wird
das elfte Aufgabenblatt nicht korrigiert.
- 15.01.2018: Das zwölfte Aufgabenblatt
ist verfügbar. Aufgrund des Warnstreiks der studentischen
Beschäftigen enthält es nur zwei Aufgaben.
- 08.01.2018: Am Freitag, den 12.01.2018 findet keine
Zentralübung statt.
- 08.01.2018: Frohes neues Jahr! Das elfte Aufgabenblatt
ist verfügbar.
- 19.12.2017: Das Tutorium am Mittwoch, 12 Uhr, wird von
Benjamin Aram Berendsohn übernommen. Das Tutorium am Mittwoch,
16 Uhr, wird von Florian Hartmann übernommen.
- 18.12.2017: Das zehnte Aufgabenblatt ist verfügbar.
- 11.12.2017: Das neunte Aufgabenblatt ist verfügbar.
- 07.12.2017: In der Abschlussklausur ist ein
beidseitig handbeschriebenes DIN-A4 Blatt mit persönlichen
Notizen zugelassen. Ansonsten sind keine Hilfsmittel erlaubt.
- 07.12.2017: Am 14.12.2017 findet in der Vorlesungszeit
eine Probeklausur statt. Die Probeklausur enthält
Aufgabentypen, die auch repräsentativ für die
Abschlussklausur sind. Die Probeklausur kann abgegeben
werden und zum Ende des Semesters verwendet werden,
um bis zu 20 Übungspunkte auszugleichen.
- 04.12.2017: Das achte Aufgabenblatt ist verfügbar.
- 27.11.2017: Das siebte Aufgabenblatt ist verfügbar.
- 20.11.2017: Das sechste Aufgabenblatt ist verfügbar.
- 13.11.2017: Das fünfte Aufgabenblatt ist verfügbar.
- 07.11.2017: Das Tutorium am Dienstag, 12–14 Uhr,
wurde in den SR055, Takustraße 9 verlegt.
- 06.11.2017: Das vierte Aufgabenblatt ist verfügbar.
- 30.10.2017: Das dritte Aufgabenblatt ist verfügbar.
- 23.10.2017: Das zweite Aufgabenblatt ist verfügbar.
- 06.10.2017: Das erste Aufgabenblatt ist verfügbar.
- 06.10.2017: Die Anmeldung zu den Tutorien ist freigeschaltet.
- 01.09.2017: Die erste Vorlesung findet
statt am Dienstag, den 17.10.2017, um 14:15.
Die Tutorien beginnen in der ersten Semesterwoche.
Die Anmeldung zu den Tutorien erfolgt über das
Online-KVV.
Ggf. gibt es noch Anpassungen der Tutoriumsgröße zu
Semesterbeginn.
- 01.09.2017: Die Website ist online.
Die Übungszettel werden ausschließlich online erscheinen
und zwar hier auf dieser Seite.
- Algorithmen
- Grundlagen
- Maschinenmodell
- Laufzeit und Speicherbedarf
- Ansätze zur Algorithmenanalyse
- O-Notation
- Lösung von Rekursionsgleichungen
- Wofür effiziente Algorithmen?
- Datenstrukturen
- Listen: einfach und doppelt verkettet
- Bitvektor
- Halden
- Suchbäume
- balanciert: AVL, rot-schwarz, ...
- extern: B-Bäume, (a,b)-Bäume
- digital: tries
- Skip-Listen
- Hashtabellen
- Konfliktbehandlung (Chaining, offene Adressierung, Kuckuck)
- universelles Hashing
- abstrakte Datentypen
- für Mengen und Listen
- Stapel, Schlange
- Prioritätswarteschlange
- Map, Ordered Map
- Iteratoren
- Relationen, Graphen, Bäume
- geometrische Objekte
- Entwurfsparadigmen
- Teile und Herrsche
- dynamisches Programmieren
- Backtracking
- gierige Algorithmen
- Plane-sweep
- Zufallsalgorithmen
- amortisierte Analyse
- diverse Algorithmen
- Graphenalgorithmen
- Breitensuche, Tiefensuche
- topologisches Sortieren
- kürzeste Weg (Dijkstra, Bellman-Ford)
- minimale aufspannende Bäume
- Suche in Zeichenketten
- Huffman-Kompression
- Spielbäume
- FFT
- Programmierung
- Merkmale guter Software
- Wiederverwendbarkeit
- Wartbarkeit
- Übersichtlichkeit
- geringe Fehleranfälligkeit
- Hilfsmittel
- Abstraktion/Geheimnisprinzip
- Modularisierung
- Generizität
- Objektorientierung
- Entwurfsmuster
- Adapter
- Iterator
- Komposition
- template method
- Komparator
- Decorator
- sprachliche Ausdrucksmittel
- Objekte und Vererbung, Zugriffsmodifizierer
- Polymorphie
- Interfaces und abstrakte Klassen
- Java collections framework
- Generics in Java
- Pakete in Java
- Annotations in Java
- Persistenz und Serialisierung in Java
- Module in Haskell
- algebraische Datentypen in Haskell
- Spezifikation und Korrektheit
- natürlichsprachliche Spezifikation
- formale Spezifikation (modellierend/algebraisch/funktional)
- Hoare-Kalkül
- Speicherverwaltung
- Keller und Haufen/Halde
- Speicherverwaltung in C/C++
- Speicherbereinigung
- Freispeicherverwaltung/Fragmentierung
- Dienstag, den 17.10.2017
- Administrativa
- Einleitung
- Es geht um Daten: wie man sie darstellt,
manipuliert, auf sie zugreift und sie speichert?
- Einführendes Beispiel: Polynome
- Darstellung eines Polynoms vom Grad n als Koeffizientenform
- Addition zweier Polynome in O(n) Operationen
- Auswerten an einer Stelle z
in O(n) Operationen mit dem Horner Schema
- Multiplikation zweier Polynome in O(n^2) Operationen
- Darstellung eines Polynoms vom Grad n durch Auswertung
an mindestens n+1 verschiedenen Stellen
- Donnerstag, den 19.10.2017
- Darstellung eines Polynoms vom Grad n durch Auswertung an mindestens n+1 verschiedenen
Stellen
- Addition zweier Polynome in O(n) Operationen
- Auswerten an einer Stelle z
in O(n^2) Operationen durch Polynominterpolation (z.B., Newton oder Lagrange Interpolation)
- Multiplikation zweier Polynome in O(n) Operationen
- Durch geschickte Wahl der Stellen kommt man von der
Koeffizienten- zur Wertedarstellung mit O(n log n)
Operationen (schnelle Fourier Transformation, FFT)
- FFT ist einer der wichtigsten Algorithmen überhaupt, mit Anwendungen
in der Signalverarbeitung, der Datenkompression, für
schnelle Quantenalgorithmen zur
Faktorisierung von großen Zahlen uvam; siehe
dazu auch Seite 27ff des MafI2 Skripts.
- Dienstag, den 24. 10. 2017
- Algorithmen: Definition und Qualitätsmerkmale
- Arten der Evaluation von Algorithmen: experimentell und theoretisch
- Experimentelle Analyse (Testen/Benchmarking): sehr konkrete Ergebnisse,
aber problematisch.
- Theoretische Analyse: liefert leicht vergleichbare und allgemeine
Aussagen, ist oft mit geringerem Aufwand verbunden als experimentelle Analyse,
kann aber auch wesentliche Aspekte vernachlässigen.
- Theoretische Analyse benötigt ein abstraktes Maschinenmodell,
um die erlaubten elementaren Schritte festzulegen.
- Definition Registermaschine/Random Access Machine (RAM)
- Definition: Laufzeit und Speicherplatz auf der RAM
- Donnerstag, den 26. 10. 2017
- Programmierung und Laufzeitbestimmung auf der RAM sind anstrengend, also: Pseudocode,
- O-Notation und asymptotische Analyse, um von konstanten Faktoren und Termen niedriger
Ordnung zu abstrahieren (Achtung: kann manchmal irreführend sein)
- Rekursion
- Dienstag, den 31. 10. 2017
- Reformationstag (keine Vorlesung)
- Donnerstag, den 02. 11. 2017 (vertreten von
Katharina Klost)
- Einfache Datenstrukturen: Stapel und Schlange
- Implementierungen: verkettete Liste oder Array fester
Grösse oder dynamisch wachsendes Array
- dynamisch wachsendes Array
- amortisierte
Analyse: betrachte eine Folge von m Operationen. Wenn
jede solche Folge f(m) Zeit benötigt, dann sind die
amortisierten Kosten pro Operation f(m)/m.
- Beweis, dass PUSH im dynamischen Array O(1) amortisierte Kosten
benötigt, mit der Buchhaltermethode: bezahle 3 € pro Operation; argumentiere,
dass dies ausreicht, um alle Kopieraktionen zu bezahlen
- Dienstag, den 07. 11. 2017
- guter Softwareentwurf: gute Software ist wartbar, flexibel, übersichtlich,
von geringer Fehleranfälligkeit
- Mittel dazu: Geheimnisprinzip
- Teile besitzen eine genau spezifizierte Schnittstelle
zur Kommunikation nach außen.
- Das Innenleben ist geheim.
- abstrakter Datentyp (ADT)
- ein Typ, dessen Objekte nur über eine klar
spezifizierte Schnittstelle manipuliert werden können, nicht durch direkten
Zugriff auf die Daten.
- Java ist eine objektorientierte Programmiersprache.
- Das zentrale Konzept sind Objekte.
- Wesentliche Merkmale sind Kapselung und Vererbung/Polymorphie.
- ADTs in Java
- Definiere Schnittstelle als Oberklasse/Interface, Implementierung als Unterklasse
- Verwende Kapselung, um die Implementierungsdetails nach außen zu
verbergen.
- Spezifikation des ADT Prioritätswarteschlange
- verbal: gib für alle Operationen Vor- und Nachbedingungen auf
natürlichsprachliche Weise an. Vorteile: einfach und verständlich;
Nachteile: lang und leicht unpräzise
- Donnerstag, den 09. 11. 2017
- Spezifikation des ADT Prioritätswarteschlange
- modellierend:
definiere ein abstraktes Modell für den Datentyp, spezifiziere
die Operationen anhand des Modells. Vorteile: knapp und genau; Nachteile:
erfordert Vorwissen, kann umständlich werden; Gefahr des Überspezifizierens.
Als Modelle können z.B. mathematische Objekte oder Haskell-Datentypen gewählt
werden.
- kurze Bemerkungen zur algebraischen Spezifikation:
kein Modell für die Daten;
Spezifikation erfolgt durch axiomatische Beziehungen der Operationen
untereinander. Eignet sich gut für mathematische und formale Analyse, wird aber
schnell kompliziert und abstrakt.
- Implementierungen des ADT Prioritätswarteschlange:
- unsortierte und sortierte verkettete Liste
- verkette Liste mit zwei Ebenen: fixiere Parameter m.
Speichere Elemente in mehreren sortierten verketteten Listen, alle bis auf die letzte mit
genau m Elementen, die letzte Liste mit höchstens m Elementen, verkette
die Minima der Listen untereinander. Bei den Operationen (vor allem deleteMin)
gibt es einen trade-off zwischen der Anzahl und der Läge der Listen.
Wenn die maximale Größe der Prioritätswarteschlagen N
bekannt ist, lässt sich Laufzeit O(sqrt(N)) pro Operation erreichen
- Dienstag, den 14. 11. 2017
- weitere Implementierungen des ADT Prioritätswarteschlange
- Donnerstag, den 16. 11. 2017
- Bottom-Up Heap Konstruktion
- ADT Wörterbuch/Dictionary/Map: kann Eintrage speichern, nachschlagen und
löschen
- Hashing
- K Schlüsselmenge, V Wertemenge, Ziel: speichere Teilmenge S von
K x V. Übliche Situation: S ist viel kleiner als K
- Idee: speichere S in einem Array, indiziert durch K. Problem:
K ist sehr groß und besteht nicht notwendigerweise aus
Zahlen. Lösung: wähle eine
Hashfunktion
h : K -> {0, ..., N-1},
welche jedem Schlüssel einen Platz in dem Array zuweist.
- Wenn K größer ist als N, kann es zu Kollisionen
kommen (zwei verschiedene Schlüssel erhalten den gleichen
Platz im Array). Möglichkeiten der
Kollisionsbehandlung:
- Verkettung: alle Einträge im selben Platz.
- offene Adressierung: wenn Platz schon besetzt, suche einen neuen.
- Kuckuck: wenn Platz schon besetzt, schaffe Platz
- Dienstag, den 21. 11. 2017
- Hashtabelle: Array +
Hashfunktion + Strategie zur Kollisionsbehandlung
- Wahl der
Hashfunktion: hashCode von K nach Z (ganze Zahlen), Kompressionsfunktion
von Z nach {0, ..., N-1}.
- Hashfunktion soll die Schlüssel möglichst gut streuen und
soll alle Muster in den Schlüsseln in S zerstören (d.h.,
h soll sich wie eine zufällige Funktion verhalten.
- Verkettung: Beschreibung der Operationen.
- Analyse von Hashing mit Verkettung
- Annahme: die Hashfunktion verhält sich zufällig.
- Dann: die erwartete Zeit pro Operation ist O(1+|S|/N).
- |S|/N heißt Belegungsfaktor. Wenn der Belegungsfaktor
O(1) ist, so auch die Laufzeit. In der Praxis möchte man den
Belegunsfaktor konstant halten (etwa bei 0,75) (-> dynamisches Array)
- Vorteile: einfach, schnell
- Nachteile: Platzbedarf, Cacheperformance
- Donnerstag, den 23. 11. 2017
- universelles Hashing
- Die Annahme einer zufälligen Hashfunktion ist
unrealistisch (müssen die zufällige Funktion
wählen und speichern).
- Aber: wir können die Funktion aus einer wesentlich kleineren
Menge wählen, und die Laufzeitanalyse gilt immer noch
- offene Adressierung mit linearem Sondieren
- speichere alle Einträge im Array
- wenn ein Platz besetzt ist, suche im Array nach dem
nächsten freien Platz
- bei suche bis zum ersten NULL Eintrag
- Markiere gelöschte Einträge mit einem speziellen
Eintrag DELETED, baue die Tabelle regelmäßig
neu auf
- Pseudocode und theoretische Garantien für offene
Adressierung
- im Erwartungswert benötigen alle Operation O(1) Zeit (wenn
der Belegungsfaktor klein genug und die Hashfunktion zufällig ist)
- Vorteile: geringer Platzbedarf, Nachteile: DELETED Einträge müllen die
Tabelle zu; Klumpenbildung (wenn viele Schlüssel in eng beieinander liegende
Positionen abgebildet werden)
- Dienstag, den 28. 11. 2017
- Kuckuck
- verwende zwei Hashfunktionen, jeder Eintrag
kann an zwei möglichen
Positionen stehen (O(1) worst-case Nachschlagen und Löschen)
- beim Einfügen kann die Position schon besetzt
sein verschiebe
den vorhandenen Eintrag an die Alternativposition.
Falls diese schon besetzt ist, verschiebe den dortigen
Eintrag, usw. Falls die Tabelle voll ist oder ein
Kreis entsteht, baue die Tabelle mit
neuen Hashfunktionen neu auf.
Pseudocode
- Wenn der Belegungsfaktor klein genug und die
Hashfunktionen zuällig sind,
so benötigt Einfügen O(1) erwartete amortisierte Zeit.
Beweis
noch ein Beweis
- Vorteile: O(1) worst-case Laufzeit für Lookups, einfach;
Nachteile: viele Verschiebeoperationen, Einfügen kann zu vollständigem
Neuaufbau der Tabelle führen
- Weitere Anwendungen von Hashfunktionen
- Hashfunktionen bieten eine Möglichkeit,
eine große, potentiell unendliche Schlüsselmenge
auf einen kleinen Bereich abzubilden.
- Dabei sollen Kollissionen möglichst unwahrscheinlich
sein: wenn der Wertebereich N Elemente besitzt, ist
bei einer zufälligen Hashfunktion die
Kollisionswahrscheinlichkeit bei 1/N.
- Wenn N mittelgroß ist, z.B.,
N=2^128 oder N=2^256, so sind Kollisionen
bei zufäliger Hashfunktion praktisch ausgeschlossen.
- Einsicht aus der Komplexitätstheorie:
Wenn eine Funktion genügend kompliziert ist,
lässt sie sich praktisch nicht von einer
zufälligen Funktion unterscheiden.
- Verwendung von komplizierten Hashfunktionen als
Message Digest/Prüfsumme/Fingerabdruck,
um die Integrität von großen Daten sicherzustellen
- Hoffnung: Ein Widersacher hat keine andere Möglickeit,
als N verschiedene zufällige Urbilder zu probieren,
bis eine Kollision gefunden ist. Der Hashwert legt
das Urbild praktisch fest.
- Anwendung in Datenstrukturen: Hashzeiger.
Speichere mit dem Verweis auf ein Objekt auch einen
Hashwert der Daten in dem Objekt. Dadurch wird die
Integriät des Zeigers sicher gestellt.
- Verwendung von Hashzeigern in einer verketteten Liste
ermöglicht ein tamper proof write only log.
Diese Datenstruktur nennt man Blockchain.
- Donnerstag, den 30. 11. 2017 (vertreten von Jonas Cleve)
- der ADT geordnetes Wörterbuch
- wie Wörterbuch, aber die Schlüsselmenge K ist geordnet.
- put(k,v), get(k), remove(k)
- max, min: liefere maximalen und minimalen Schlüssel
- pred(k): liefere den größten Schlüssel kleiner k im
Wörterbuch (Vorgänger)
- succ(k): liefere den kleinsten Schlüssel größer k im
Wörterbuch (Nachfolger)
- Skiplisten
- speichere eine Hierarchie von Listen L0, L1, ....
L0 speichert alle Einträge, L(i+1) speichert jeden
Eintrag von L(i) mit Wahrscheinlichkeit
- Pseudocode für die Operationen
- Erwarteter Platzbedarf O(n), erwartete Höhe O(log n),
erwartete Suchzeit O(log n), wenn n Einträge gespeichert
werden
- Originalarbeit
- Dienstag, den 05. 12. 2017
- Analyse von Skiplisten: erwartete Suchzeit ist O(log n).
- Bäume: Baum, gewurzelter Baum, Höhe, Blatt, Elternknoten,
Kindknoten, binärer Baum
- binärer Suchbäum: gewurzelter geordneter binärer
Baum, welcher die Einträge in den Knoten speichert und die
binäre Suchbaumeigenschaft hat: für jeden
Knoten mit Schlüssel k sind alle Schlüssel im linken
Teilbaum kleiner als k und alle Schlüssel im rechten
Teilbaum größer als k.
- Donnerstag, den 07. 12. 2017 (vertreten von Jonas Cleve)
- Operationen auf einem binären Suchbaum am Beispiel.
- Pseudocode für die Operationen.
- Alle Operationen haben worst-case Laufzeit O(Höhe des
Baums).
- Im schlimmsten Fall kann der Baum degenerieren und Höhe
n-1 haben (z.B., wenn man 1, 2, ..., n in dieser Reihenfolge
einfügt).
- AVL-Bäume
- Dienstag den 12. 12. 2017 (vertreten von Jonas Cleve)
- AVL-Bäume
- höhenbalancierte Bäume; für jeden inneren
Knoten unterscheiden sich die Höhen der
Unterbäume um
höchstens 1.
- Die Höhenbalancierung gewährleistet
Höhe O(log n).
- Strukturelle Operationen auf Binärbaumen:
- Einfachrotationen: Linksrotation
(l-Rotation), Rechtsrotation (r-Rotation)
- Doppelrotationen: lr-Rotation (erst links, dann rechts),
rl-Rotation (erst rechts, dann links)
- Wenn u ein Knoten ist, bei dem sich die Höhen der
Unterbäume um genau 2 unterscheiden, und alle
Nachkommen von u ausgeglichen sind, so gibt es immer eine
(l-,r-,lr-, oder rl-)Rotation, die den Teilbaum bei u
ausgleicht.
- Einügen und Löschen in AVL-Bäume:
füge ein bzw. lösche wie in einem herkömmlichen
BSB, verfolge dann den Pfad zur Wurzel zurück und
führe geeignete Rotationen durch, um alle Knoten auf dem
Pfad auszugleichen.
- Vorteile: O(log n) worst-case Laufzeit; geringer
Speicherbedarf
- Nachteile: umständliche Implementierung
(Fallunterscheidung); ggf. sind viele
Rotationen nötig
- Bemerkungen zu
rot-schwarz Bäumen
- Lockern die Baumstruktur im Vergleich zu AVL-Bäumen weiter,
um weniger Umstrukturierungen beim Einfügen und Löschen
vornehmen zu müssen
- Jeder Knoten ist rot oder schwarz. Die Färbung
muss gewisse Bedingungen erfüllen. Dies
gewährleistet Höhe O(log n).
- Einfügen und Löschen ist ähnlich wie in
AVL-Bäumen, aber nun kann die Struktur auch durch
Umfärben der Knoten wiederhergestellt werden. Dadurch muss
man weniger oft rotieren, aber es sind viele
Fälle zu unterscheiden
- Populäre Datenstruktur, die oft implementiert wird.
Zum Beispiel in der Java-Bibliothek, der C++-STL,
oder im Completely-Fair-Scheduler des Linux-Kernel.
- Donnerstag, den 14. 12. 2017
- Dienstag, den 19. 12. 2017
- (a,b)-Bäume
- a,b sind natürliche Zahlen, mit b >= 2a-1 und
a >= 2.
- Jeder Knoten hat Grad höchstens b. Die Wurzel hat
Grad mindestens 2, jeder andere innere Knoten hat
Grad mindestens a.
-
Die Wurzel speichert zwischen 1 und b-1 Schlüssel,
alle anderen Knoten speichern zwischen a-1 und b-1
Schlüssel,
- Die binäre Suchbaumeigenschaft wird entsprechend auf mehrere
Kinder verallgemeinert.
- Alle Blätter haben die gleiche Tiefe.
- Die Tiefe ist O(log n/log a).
- Die Suche funktioniert wie bei BSBs, entsprechend
angepasst für die Mehrwegknoten.
Die Laufzeit ist O(b log n/log a).
- Einfügen in (a,b)-Bäumen: suche bis zum Blatt, füge den Eintrag
in das entsprechende Blatt ein. Wenn das Blatt überläuft
(b Einträge), spalte es und füge den mittleren
Schlüssel in den Elternknoten ein. Falls nötig,
spalte diesen, usw., bis zur Wurzel.
Laufzeit O(b log n / log a).
- Löschen in (a,b)-Bäumen
- Wenn der zu löschende Eintrag in einem
inneren Knoten liegt, ersetze ihn durch seinen Nachfolger
und lösche diesen aus einem Blatt.
- Löschen aus einem Blatt: entferne den Eintrag,
wenn das Blatt mindestens
a Einträge enthält, ist nichts zu tun;
ansonsten, versuche, einen Eintrag von einem Geschwisterknoten
zu borgen; wenn das nicht geht, verschmelze mit einem
Geschwisterknoten; evtl. läut nun der Elternknoten
unter, wiederhole, die Prozedur bis es nicht mehr
nötig ist.
- Anwendungen: (2,4)-Bäume sind äquivalent zu
Rot-Schwarz-Bäumen; als externe Datenstrukturen
in Datenbanksystemen oder Dateisystemen
(z.B., Btrfs,
NTFS)
- Mehr Details zu den
Operationen gibt es hier.
- Donnerstag, den 21. 12. 2017
- Zeichenketten: endliche Folgen von Symbolen aus einem
Alphabet Sigma
- Kodes:
- weise jedem Symbol aus dem Alphabet eine Bitfolge zu.
- Zum Kodieren eines Strings, hänge die entsprechenden
Bitfolgen aneinander.
- Desiderata: (i) eindeutige Dekodierbarkeit, (ii)
möglichst kurzes Kodewort
(alternativ: (ii) hohe Fehlertoleranz)
- präfixfreie Kodes sind Kodes, bei denen kein
Kodewort Präfix eines anderen Kodeworts sind
- präfixfreie Kodes lassen sich immer eindeutig
dekodieren
- präfixfreie Kodes lassen sich als Baum
darstellen
- Huffman-Kompression
- liefert einen optimalen Präfixkode für gegebene
Häufigkeiten
- gieriger Algorithmus: treffe lokal optimale Entscheidungen,
und hoffe, ein global optimales Ergebnis zu erzielen
- Baue den Kodierbaum inkrementell: starte mit einem Blatt
für jedes Zeichen, beschrifte die Blätter mit den
Häufigkeiten; vereinige diejenigen Unterbäume, welche
die geringsten Häufigkeiten haben, addiere die
Häufigkeiten, wiederhole, bis nur noch ein Baum übrig
ist. Das Ergebnis heißt Huffman-Baum
- Dienstag, den 09. 01. 2018
- Huffman-Kodes sind optimal.
- Lemma: Für zwei Zeichen a, b mit geringsten
Häufigkeiten existiert immer ein optimaler
Präfixkode, in dem a und b Geschwister sind.
- Beweis des Satzes durch Induktion nach der
Alphabetgröße k: für k=2 ist die
Aussage klar. Argument für den Schritt: wenn es
für ein
Alphabet der Größe k einen besseren
Präfixkode gäbe als den
Huffman-Kode, könnte man mit Hilfe des Lemmas auch
einen besseren Präfixkode
als den Huffmankode für Alphabetgröße k-1
konstruieren, im Widerspruch zur Induktionsannahme.
- Bemerkungen zur Datenkompression
- Huffmankodes sind optimal, aber wir machen
drei Annahmen: 1. der Kode ist präfixfrei;
2. wir kodieren Zeichen für Zeichen;
3. die Kompression ist verlustfrei
- Annahme 2 kann man auf verschiedene Weisen umgehen:
(i) Lauflängenkodierung:
speichere sich wiederholende Zeichen nur einmal mit der Anzahl.
Verwendet z.B. für das
PCX-Bildformat.
(ii) Kode mit Wörterbuch: z.B.
LZW.
Sich wiederholende Zeichenketten werden durch ein Zeichen abgekürzt. Benutzt
z.B. im
GIF Format.
LZW war seinerzeit patentiert, was große Diskussionen auslöste.
- Um Annahme 3 zu umgehen nehmen wir an, dass es akzeptabel
ist, unwichtige Informationen zu entfernen. Unwichtige Information
wird durch eine geeignete Transformation der Daten identifiziert.
Verwendet z.B. in JPEG
oder MP3
- Donnerstag, den 11. 01. 2018
- Ähnlichkeit von Zeichenketten: diff, fc, längste
gemeinsame Teilfolge, Editierabstand
- finden einer lägsten gemeinsamen Teilfolge (LGT) der Zeichenketten
s und t.
- Zunächst berechnen wir nur die Länge einer solchen
LGT (LLGT).
- Rekursiver Ansatz: betrachte das jeweils letzte Zeichen von s und t
(oder auch das erste). Wenn diese beiden Zeichen gleich sind,
so ist LLGT= 1 + die
LLGT von s' und t', die Wörter, die man aus s und t erhält,
indem man jeweils das letzte Zeichen streicht. Wenn diese beiden Zeichen
nicht gleich sind, bestimme rekursiv
die LLGTs von s' und t sowie von s und t' und nimm das Maximum
der beiden Lösungen.
- Sobald man diese Rekursion hat, kann man sie unmittelbar
als Formel aufschreiben und implementieren.
- Problem: Die Implementierung ist ineffizient und hat exponentielle
Laufzeit.
- Aber: Viele Teilprobleme wiederholen sich. Daher können wir
eine Tabelle zur Speicherung der Zwischenergebnisse benutzen.
Dies nennt man dynamisches Programmieren.
- Zwei Möglichkeiten der Implementierung: (i) top down als
Rekursion, die aber immer in der Tabelle nachschaut, ob ein erneuter
Rekursionsaufruf wirklich nötig ist; (ii) bottom up, indem
man die Tabelle von unten durch Schleifen auffüllt.
Möglichkeit (ii) ist oft vorzuziehen, da sie zu kürzerem
und effizienterem Code führt.
- Sobald man die Tabelle hat, kann man die LGT bestimmen, indem
man einen optimalen Weg durch die Rekursion rekonstruiert
(bzw. in einer zweiten Tabelle mitspeichert).
- Eine Beispielimplementierung im Pseudocode gibt es
hier.
- Dienstag, den 16. 01. 2018
- Suche in Zeichenketten.
Der naive Algorithmus und der Algorithmus von
Rabin-Karp: Notizen.
- Donnerstag, den 18. 01. 2018
- Wörterbücher für Zeichenketten: Tries
- Tries sind eine spezialisierte Datenstruktur, die eine
Menge von Zeichenketten speichern und die Operationen
des ADT geordnetes Wörterbuch zur Verfügung stellen.
- Ein Trie ist ein gewurzelter Mehrwegbaum: jeder Knoten hat
zwischen einem und S Kinder, wobei S = |Sigma| die
Alphabetgröße ist.
- Die Kanten sind mit Zeichen aus Sigma beschriftet, so
dass für jeden Knoten jedes Zeichen höchstens
einmal als Label einer Kindkante vorkommt.
- Die gespeicherten Zeichenketten lassen sich erhalten, indem
man für jedes Blatt v die Kantenbeschriftungen von
der Wurzel zu v aneinanderhängt.
- Problem: Was, wenn ein String Präfix eines anderen ist?
Lösung 1: Führe zusätzliche Markierungen im
Baum ein, um das Wortende anzuzeigen. Nachteil: Verkompliziert
die Algorithmen und die Strukture.
Lösung 2: Führe ein spezielles Zeichen ein, welches das
Wortende anzeigt und nicht im Alphabet vorkommt, und füge
dieses Zeichen implizit an jede gespeicherte Zeichenkette an.
Beachte: Lösung 2 ist im wesentlichen äquivalent zu
Lösung 1, aber konzeptionell simpler, da man die Algorithmen
einfacher halten kann.
- Tries unterstützen die Operationen des ADT
geordnetes Wörterbuch in Zeit O(r|Sigma|), wobei
r die Gesamtläange der Zeichenketten ist, die von der
Operation angefragt bzw. zurückgegeben werden.
- Der Platz für einen Trie ist proportional zur Gesamtlänge
der darin gespeicherten Schlüssel.
- Komprimierte Tries/Patricia-Tries: ersetze lange Ketten von
Knoten mit nur einem Kind durch eine Kante, die mit dem entsprechenden
Teilstring beschriftet ist. Dadurch hat der Trie nur O(l) Knoten
(l = Anzahl der im Trie gespeicherten Schlüssel. Asymptotisch
ändert sich aber nichts am Platzbedarf.
- Suffixbaum: ein komprimierter Trie für alle Suffixe einer
gegebenen Zeichenkette s. Um die Gesamtgröße linear
zu halten, speichert man an den Kanten anstelle von
Teilstrings aus s nur Zeiger an die entsprechenden Stellen
in s.
- Ein Suffixbaum lässt sich in mit einem
speziellen Algorithmus in linearer Zeit berechnen
(Das naive Einfügen aller Suffixe könnte quadratische Zeit
benötigen).
- Suffixbäume haben viele Anwendungen im Bereich der Stringsuche.
Ein paar sind auf der
Wikipedia-Seite aufgelistet.
- Graphen–Grundbegriffe, Beispiele
- Dienstag, den 23. 01. 2018
- einige Algorithmische Probleme auf Graphen:
Sind zwei Knoten verbunden? Zusammenhangskomponenten
finden; kürzeste Wege finden (ohne und mit
Kantengewichten); topologisches Sortieren;
Bestimmung eines minimalen aufspannenden Baums;
Bestimmung einer minimalen Kantenmenge, die man
entfernen muss, um Knoten zu trennen.
- Zwei Möglichkeiten der Darstellung im Rechner:
Adjazenzmatrix und Adjazenzliste. Im Allgemeinen
liefern Adjazenzlisten eine bessere Laufzeit,
wenn der Graph nicht zu viele Kanten hat.
- Tiefensuche und Breitensuche
Siehe hier.
- Donnerstag, den 25. 01. 2018
- Breitensuche
- Single Pair Shortest Path Problem: Finde einen kürzesten Weg
von s nach t.
- Single Source Shortet Path Problem: Finde kürzeste Wege von
s zu allen anderen Knoten im Graphen
- Teilpfadoptimalitätseigenschaft: Teilwege eine kürzesten
Weges sind wieder kürzeste Wege
- Darstelleng der kürzesten Wege von s zu allen anderen Knoten:
als kürzeste-Wege-Baum: jeder Knoten v speichert einen
Zeiger zu seinem Vorgänger auf einem kürzesten Weg von s nach v.
Man findet den kürzesten Weg selber, indem man den
Vorgängerzeigern folgt
- Kürzeste Wege in ungewichteten Graphen: modifiziere
BFS so dass sie Vorgängerzeiger und Abstände zu s
speichert. Siehe hier.
- Dienstag, den 30. 01. 2018
- kürzeste Wege in gewichteten Graphen:
der
Algorithmus von Dijkstra
- Annahme: alle Kantengewichte sind nichtnegativ
- Modifiziere den BFS-Algorithmus für ungewichtete
Graphen auf zwei Arten: 1. verwende eine
Prioritätswarteschlange statt eine Warteschlange,
2. wenn die Nachbarn eines Knoten untersucht werden,
müssen wir überprüfen, ob sich der Weg
zum Nachbarn verkürzen lässt
- Pseudocode hier.
- Die Laufzeit hängt ab von der Implementierung
der Prioritätswarteschlange. Wir führen
|V| inserts, |V| extractMins, und O(|E|)
decreaseKeys durch.
Wenn ein binärer Heap zugrunde liegt,
ist die Laufzeit O(|V| log |V| + |E| log |V|).
- Dijkstra: Korrektheitsbeweis
- Grundidee: Nenne einen Knoten verarbeitet,
wenn er nicht mehr in der Prioritätswarteschlange
enthalten ist. Die Werte v.d und v.pred beziehen sich auf
kürzeste Wege, die nur Knoten benutzen, die schon
verarbeitet sind (ggf. mit Ausnahme von v). Alle Wege
zu noch nicht verarbeiteten Knoten sind mindestens so
lang wie alle kürzesten Wege zu schon verarbeiteten Knoten.
Wenn wir einen neuen Knoten verarbeiten, bleiben all
diese Invarianten erhalten, da alle Kantengewichte
nichtnegativ sind und wir den Knoten mit minimalem
Abstand verarbeiten.
- Donnerstag, den 01. 02. 2018
- A*-Suche mit konsistentem Schätzer:
- Wollen einen kürzesten s-t-Pfad.
- Zusätzlich zu dem Graphen kennen wir noch
einen Schätzer: eine Funktion
h: V -> R+, die den Abstand von jedem Knoten
v nach t schätzt.
- h heißt konsistent, wenn für jede
Kante e=uv gilt:
h(u) <= e.length + h(v)
- A*-Suche entspricht Dijkstras Algorithmus mit
veränderten Kantengewichten: Für eine Kante
e = uv ist
e.length' := e.length + h(v) - h(u)
- Konsistenz stellt sicher, dass die neuen length' Längen
nichtnegativ sind.
- Kanten, die auf das Ziel zuführen werden kürzer,
Kanten, die vom Ziel weggehen werden länger.
- Die Länge eines jeden Weges von s nach t
ändert sich um h(t) - h(s) (Teleskopsumme). Daher
ist ein kürzester Weg für die neuen Gewichte
auch ein kürzester Weg für die alten Gewichte (die
Änderung hängt nicht vom Weg ab, nur von Endpunkten).
- Wenn der Schätzer gut ist, müssen wir wesentlich
weniger Knoten besuchen als bei normalem Dijkstra
- Wenn der Schätzer gut ist, müssen wir wesentlich
- Anwendungen: Routenplanung; Suchprobleme in der künstlichen Intelligenz
- Dienstag, den 06. 02. 2018
- All-Pairs-Shortest-Paths: Floyd-Warshall
- APSP-Problem: Finde einen kürzesten Weg von u nach v
für alle Knotenpaare u und v.
- Wenn alle Kantengewichte nichtnegativ sind und der Graph wenig
Kanten enthält, kann man Dijkstra für jeden
Knoten ausführen
- Allgemeiner Algorithmus mit dynamischem Programmieren:
nummeriere Knoten beliebig von 1 bis n. Für k = 0 bis n,
finde rekursiv die Längen aller kürzeten Wege, welche
nur Zwischenknoten mit Nummer 1 bis k benutzen.
- Der Algorithmus von Floyd-Warshall lässt sich mit
O(n^3) Zeit und O(n^2) Platz implementieren (n = Anzahl der Knoten).
Mit O(n^3) Platz kann man auch die kürzesten Wege selbst finden.
- MST-problem (minimum spanning tree)
- gegeben: Graph G=(V, E), ungerichtet, gewichtet,
zusammenhängend
- gesucht: Kantenmenge T mit minimalem Gewicht, so
dass (V, T) ein Baum ist
- allgemeine Strategie: gierig, beginne mit leerer
Kantenmenge, füge sukzessive sichere
Kanten hinzu.
- Eine Kantenmenge A heißt sicher,
falls es einen MST gibt, der A enthält
- Schnittlemma: Sei A eine sichere Kantenmenge,
und S ein Schnitt, welcher von keiner Kante aus
A überquert wird. Sei e eine Kante von minimalem
Gewicht, die S überquert. Dann ist A vereinigt
mit e sicher.
- Donnerstag, den 08. 02. 2018
- Beweis des Schnittlemmas
- Benutze Schnittlemma, um sichere Kanten zu
finden.
- Zwei Umsetzungen: der Algorithmus von Prim-Jarník-Dijkstra und
der Algorithmus von Kruskal
- Prim-Jarník-Dijkstra: lasse den MST von einem Startknoten
s aus wachsen, nimm immer die leichteste Kante inzident
zur aktuellen Zusammenhangskomponente.
Implementierung
wie bei Dijkstras Algorithmus für
kürzeste Wege, nur mit anderem Gewichtsmanagement.
Selbe Laufzeit wie bei Dijkstra.
- Der Algorithmus von Kruskal: sortiere die Kanten, nimm immer die nächste
Kante, die zwei verschiedene Zusammenhangskomponenten
verbindet
- Dienstag, den 13. 02. 2018
- Komplexitätstheorie: Berechenbarkeitstheorie und
Komplexitätstheorie; Effizienzbegriff; These von
Cobham-Edmonds; Erweiterte Church-Turing These
- Entscheidungsprobleme, Laufzeit, Turingmaschinen,
Komplexitätsklassen
- Die Komplexitätsklasse P, die Menge aller
effizient entscheidbarer Sprachen: formale Definition und
Beispiele.
- Zeithierarchiesatz: Es gibt "beliebig schwierige"
entscheidbare algorithmische Probleme.
- Probleme, die wahrscheinlich nicht in P liegen:
Sudoku und
Sokoban.
- Wenn ein Sudokupuzzle eine Lösung hat, kann man diese
effizient aufschreiben und überprüfen.
- Bei Sokoban ist das wahrscheinlich nicht so. Wenn ein
Sokoban-Puzzle eine Lösung hat,
kann diese extrem lang sein, so dass man sie nicht effizient darstellen kann
- Donnerstag, den 15. 02. 2018
- NP: Die Klasse der Entscheidungsprobleme, für die es effizient überprüfbare
Zertifikate von polynomieller Länge gibt.
- Alle Probleme aus P sind in NP. Sudoku ist in NP, Sokoban wahrscheinlich nicht.
- Weitere Probleme in NP: Clique, Rucksack, Circuit-SAT.
- Reduktionen und NP-vollständige Probleme
- Sudoku ist NP-vollständig,
Sokoban ist PSPACE-vollständig.
- Das P-NP-Problem
- Schluss
- Dienstag, den 20. 02. 2018
- Montag, den 26. 03. 2018
- Donnerstag, den 12. 04. 2018
- Dienstag, den 24. 04. 2018 (geplant, SR006)
- Einsicht der Klausur und der Nachklausur
Empfohlene Vorkenntnisse
- grundlegende diskrete Mathematik, Logik und
Wahrscheinlichkeitstheorie
- Programmierkenntnisse in Haskell, Python und Java
Literatur
- P. Morin:
Open Data Structures, an open content textboox:
[link]
- T. H. Cormen, C. Leiserson, R. Rivest, C. Stein:
Introduction to Algorithms, MIT Press, 2009:
Zur Ergänzung empfohlen. Sehr umfangreich, geht weit
über die Vorlesung hinaus. Sehr formal, sieht manchmal
den Wald vor lauter Bäumen nicht.
- R. Sedgewick:
Algorithms in Java (Part 1–5), Addison-Wesley, 2003:
Hier steht auch alles drin, aus etwas anderer Sicht
erklärt. Sehr ausführlich
bei den Sortieralgorithmen.
- G. Saake, S. Sattler:
Algorithmen und Datenstrukturen, dpunkt.verlag, 2013:
Ein Buch auf deutsch, deckt vieles ab, geht aber nicht
so sehr in die Tiefe.
- M. Dietzfelbinger, K. Mehlhorn, P. Sanders:
Algorithmen und Datenstrukturen: Die Grundwerkzeuge, Springer, 2014:
[englisch]
[deutsch]
- M.T. Goodrich, R. Tamassia:
Data Structures and Algorithms in Java, Wiley, 2014:
Deckt den Vorlesungsstoff größtenteils ab,
aufgrund der inakzeptablen Preispolitik nicht zur Anschaffung empfohlen.
Es wird eine Klausur am Ende des Semesters geben,
sowie eine Ersatzklausur vor Beginn des
nächsten Semesters.
Dabei gilt die Freiversuchsregel, d.h., man kann die Ersatzklausur nutzen,
um das Ergebnis aus der Hauptklausur
zu verbessern (nur bei erstmaligem Absolvieren des Moduls).
Die Klausur findet statt am Dienstag, den 20. Februar 2018, von
14–16 Uhr, im Henry-Ford-Bau.
Die Nachklausur findet statt am Donnerstag, den 12. April 2018, von
10–12 Uhr, im Hörsaal 2 der Silberlaube.
Für den Scheinerhalt muss man
-
regelmäßig an einem dezentralen
Tutorium teilnehmen (bei mindestens 80% der Termine);
-
mindestens 60% der Punkte auf den Übungszetteln erreichen und
mindestens zweimal im Tutorium vortragen; und
- mindestens 50% der Punkte in der Abschlussklausur erreichen.
Die erfolgreiche Teilnahme am Übungsbetrieb und das Bestehen der
Abschlussprüfung sind unabhängige Leistungen.
Das Modul wird benotet. Die Note beruht nur auf dem Ergebnis
der Abschlussprüfung.
Die Vorlesung findet statt Dienstag und Donnerstag, 14–16 Uhr,
im großen Hörsaal in der Takustraße 9.
Zusätzlich gibt es eine Zentralübung. Diese wird von Katharina
Klost gehalten und findet
Freitag von 14–16 Uhr im großen Hörsaal in der
Takustraße 9 statt. In der Zentralübung werden
die Übungausfgaben besprochen und vertieft. Die Teilnahme
an der Zentralübung ist freiwillg.
Die dezentralen Tutorien werden von vier studentischen Tutor_innen
durchgeführt (Benjamin Aram Berendsohn, Tobias Gleißner,
Florian Hartmann und Anja Wolffgramm).
Dienstag
|
12:00–14:00 |
Takustraße 9, Seminarraum 055 |
Benjamin Aram Berendsohn |
16:00–18:00 |
Takustraße 9, Seminarraum 051 |
Florian Hartmann |
Mittwoch |
08:00–10:00 |
Takustr. 9, Seminarraum 046 |
Benjamin Aram Berendsohn |
08:00–10:00 |
Takustr. 9, Seminarraum 053 |
Tobias Gleißner |
|
10:00–12:00 |
Takustraße 9, Seminarraum 055 |
Anja Wolffgramm |
|
12:00–14:00 |
Takustraße 9, Seminarraum 006 |
Florian Hartmann |
12:00–14:00 |
Takustraße 9, Seminarraum 051 |
Benjamin Aram Berendsohn |
12:00–14:00 |
Takustraße 9, Seminarraum 055 |
Anja Wolffgramm |
|
16:00–18:00 |
Arnimallee 6, Seminarraum 032 |
Tobias Gleißner |
16:00–18:00 |
Takustraße 9, Seminarraum 049 |
Florian Hartmann |
Freitag |
14:00–16:00 |
Takustr. 9, großer Hörsaal |
Zentralübung Katharina Klost |
Sprechzeit des Dozenten: Dienstag 13–14 Uhr in Zimmer 112 und
nach Vereinbarung.
Die Übungszettel werden jede Woche hier verlinkt. Sie werden
ausschließlich online erscheinen.
Die normale Bearbeitungszeit ist von Montag bis zum Freitag
11 Tage später. Die Zettel sind bis 10 Uhr
in die entsprechenden Tutorenfächer abzugeben. Die Bearbeitung
erfolgt in Zweiergruppen. Eine LaTeX-Schablone für die
Übungszettelabgabe findet sich hier
(mit Beispielprogramm).
Übungszettel, die nach dem angegebenen Termin
abgegeben werden, zählen als nicht bearbeitet.
Nr. | Ausgabe | Abgabe |
pdf | tex | Bemerkungen |
1 |
06.10.2017 |
27.10.2017 |
|
|
|
2 |
23.10.2017 |
03.11.2017 |
|
|
|
3 |
30.10.2017 |
10.11.2017 |
|
|
|
4 |
06.11.2017 |
17.11.2017 |
|
|
|
5 |
13.11.2017 |
24.11.2017 |
|
|
Heaps |
6 |
20.11.2017 |
01.12.2017 |
|
|
Hashing (aktualisiert!)
Aufgabe 2(c) verbessert |
7 |
27.11.2017 |
08.12.2017 |
|
|
|
8 |
04.12.2017 |
15.12.2017 |
|
|
Skiplisten
Kuckucks-Hashing
binäre Suchbäume
|
9 |
11.12.2017 |
22.12.2017 |
|
|
|
Probeklausur |
14.12.2017 |
keine |
|
|
Englisch
|
10 |
18.12.2017 |
12.01.2018 |
|
|
Weihnachtszettel
|
11 |
08.01.2018 |
19.01.2018 |
|
|
(a, b)-Bäume
|
12 |
15.01.2018 |
26.01.2018 |
|
|
LCS
Suche in Zeichenketten
|
13 |
22.01.2018 |
02.02.2018 |
|
|
Vorletztes Aufgabenblatt.
|
14 |
29.01.2018 |
09.02.2018 |
|
|
Letztes Aufgabenblatt.
|