Übungsblatt 6 - Institut für Informatik - Humboldt

Werbung
Humboldt-Universität zu Berlin
Institut für Informatik
Prof. Dr. Ulf Leser
M. Bux, B. Grußien, J. Sürmeli, S. Wandelt
Berlin, den 29.6.2015
Übungen zur Vorlesung
Algorithmen und Datenstrukturen
Übungsblatt 6
Abgabe: Montag den 13.7.2015 bis 11:10 Uhr vor der Vorlesung im Hörsaal oder bis 10:45 Uhr
in den Fächern im Raum RUD25 4.402. Die Übungsblätter sind in Gruppen von 2 Personen zu
bearbeiten. Sie können auf diesem Übungsblatt bis zu 50 Punkte erhalten. Zur Erinnerung:
Jedes Übungsblatt muss bearbeitet werden. Sie müssen mindestens ein Blatt für wenigstens eine Aufgabe jedes Übungsblattes abgeben. Die Lösungen sind auf nach Aufgaben
getrennten Blättern abzugeben. Vermerken Sie auf allen Abgaben Ihre Namen, Ihre
Matrikelnummern, den Namen Ihrer Goya-Gruppe und welchen Übungstermin bei welchem
Übungsleiter Sie besuchen. Heften Sie bitte die zu einer Aufgabe gehörenden Blätter
vor der Abgabe zusammen.
Beachten Sie auch die aktuellen Hinweise auf der Übungswebsite unter:
https://u.hu-berlin.de/alg_ds_ss15_u
Konventionen:
• Mit der Aufforderung “Analysieren Sie die Laufzeit” ist hier gemeint, dass Sie eine
möglichst gute obere Schranke der Zeitkomplexität angeben sollen und diese begründen
sollen.
Aufgabe 1 (AVL-Bäume-Schreibtischtest)
7 + 7 = 14 Punkte
Führen Sie einen Schriebtischtest für die folgenden zwei Aufgaben zu AVL-Bäumen aus.
1. Sei T ein leerer AVL-Baum. Fügen Sie nacheinander die Schlüssel
5, 17, 89, 23, 13, 2, 28, 26, 102, 18, 19, 117
in T ein und zeichnen Sie den jeweiligen AVL-Baum nach jeder insert-Operation.
2. Entfernen Sie nacheinander die Schlüssel 75, 87, 70 aus dem unten gegebenen AVL-Baum
und zeichnen Sie den jeweiligen AVL-Baum nach jeder delete-Operation. Falls ein Knoten
mit zwei Kindern gelöscht wird, dann soll mit dem symmetrischen Vorgänger (siehe Folie
32 im Foliensatz Suchbäume) getauscht werden.
Aufgabe 2 (Optimale Suchbäume)
6 Punkte
Sei S = {s1 , . . . , sn } eine Menge von paarweise verschiedenen Schlüsseln und seien h1 , . . . , hn
paarweise verschiedene absolute Häufigkeiten, mit denen auf diese Schlüssel zugegriffen wird,
d. h., Schlüssel si wird hi -mal abgefragt. Weiterhin nehmen wir an, dass es keine Zugriffe auf
Schlüssel außerhalb von S gibt.
Betrachten Sie den folgenden Greedy-Algorithmus zur Konstruktion eines binären Suchbaums:
Wähle den Schlüssel x, auf den am häufigsten zugegriffen wird, als Wurzel. Von den
Schlüsseln, die kleiner sind als x und als linkes Kind von x in Frage kommen, ohne gegen die Suchbaumeigenschaft zu verstoßen, wähle denjenigen, auf den am häufigsten
zugegriffen wird, als linkes Kind von x. Analog wähle von den Schlüsseln, die größer
sind als x und deren Einfügen als rechtes Kind von x die Suchbaumeigenschaft nicht
verletzen würde, den Schlüssel, auf den am häufigsten zugegriffen wird, als rechtes
Kind von x. Fahre iterativ fort, bis alle Schlüssel eingefügt wurden.
Unter der Suchbaumeigenschaft ist zu verstehen, dass für jeden Knoten v im Suchbaum gilt,
dass die Schlüssel aller Knoten im linken Teilbaum von v kleiner und im rechten Teilbaum von
v größer als der Schlüssel von v sind.
Zum Beispiel würde der o.g. Greedy-Algorithmus für die Schlüssel 10, 2, 12 und 20 mit den
absoluten Zugriffshäufigkeiten 100, 20, 10 und 90 den folgenden binären Suchbaum generieren.
Beweisen oder widerlegen Sie, dass der obige Algorithmus unter den genannten Bedingungen
einen optimalen Suchbaum erzeugt.
Aufgabe 3 (Tries, Implementierung)
4 + 4 + 4 = 12 Punkte
Zur Speicherung einer Menge von Wörtern zum Zweck eines effizienten Zugriffs haben Sie in der
Vorlesung Tries kennengelernt. Dabei handelt es sich um Bäume, deren Kanten mit Buchstaben
beschriftet sind, wodurch gemeinsame Präfixe unterschiedlicher Wörter nur einmal gespeichert
werden müssen. Aufgrund dieser Eigenschaft werden Tries auch als Präfixbäume bezeichnet. Um
in einem solchen Trie das Ende eines Wortes, welches zugleich das Präfix eines anderen Wortes
ist, erkennen zu können, kann man sich des Tricks bedienen, an jedes zu speichernde Wort ein
$-Zeichen anzuhängen, wodurch das Ende eindeutig markiert ist. Im Folgenden finden Sie ein
Beispiel für einen Trie, in den die Wörter i, in, inn, so, son und sky eingefügt wurden:
s
$
$
n
o
i
k
y
$
$
n
$
n
$
Ihre Aufgabe ist es, die Implementierung eines solchen Tries zu vervollständigen. Nutzen Sie
hierzu die auf der Homepage zur Verfügung gestellte Datei Trie.java und ergänzen Sie die
unten aufgeführten Methoden um fehlenden Code. An bestehendem Code soll nichts verändert
werden. Sie dürfen aber die main()-Methode zum Testen verwenden und auch um Ihre eigenen
Testfälle erweitern.
1. Implementieren Sie die Methode Trie.add(String word), die ein gegebenes Wort in den
bestehenden Baum einfügt.
2. Implementieren Sie die Methode Trie.contains(String word), die überprüft, ob ein
gegebenes Wort im Baum vorhanden ist, und entsprechend true oder false zurückgibt.
3. Implementieren Sie die Methode Trie.printWords(), die alle im Trie enthaltenen Wörter
ausgibt.
Hinweis: Ihr Java-Programm muss auf dem Rechner gruenau2 kompilieren und laufen. Die
Abgabe dieser Aufgabe erfolgt über Goya.
Aufgabe 4 (Graphalgorithmen, Dijkstra)
7 + 6 + 3 + 2 = 18 Punkte
1. Stellen Sie sich vor, dass es in Ihrer Lieblingsstadt keine Verkehrszeichen gibt – d. h. an
allen Kreuzungen muss die Regel rechts-vor-links beachtet werden. Außerdem nehmen wir
an, dass an jeder Kreuzung maximal 4 Straßen aufeinander treffen. Somit ist klar definiert,
was in Fahrtrichtung rechts, links und geradeaus ist.
Wir modellieren die Straßen der Stadt wie folgt als ungerichteten Graphen: Jede Kreuzung
wird ein Knoten des Graphen und jede Straße, die zwei Kreuzungen verbindet, wird eine
Kante. Wir nehmen an, dass Straßen unterschiedlich schnell durchfahren werden können
und dies modellieren wir mit nichtnegativen Kantenkosten. Hier ein Beispiel:
h
1
d
1
1
i
e
2
8
a
2
f
2
b
5
6
4
c
g
Rechts-vor-links-Kreuzungen haben die folgende Eigenschaft: Will man rechts abbiegen,
so muss man auf niemanden achten, will man geradeaus fahren, so muss man auf den
Verkehr von rechts achten, will man links abbiegen, so muss man auf den Verkehr von
rechts und von geradeaus achten. Wir führen deshalb noch Kosten für das Abbiegen ein:
Muss man auf niemanden achten, dann kostet das Überfahren der Kreuzung 1, muss man
auf eine Richtung achten, dann kostet das Überfahren der Kreuzung 2, muss man auf zwei
Richtungen achten, dann kostet das Überfahren der Kreuzung 3. Zum Beispiel würde der
Pfad e → f → i im obigen Graph 8 + 3 + 2 kosten, der Pfad a → b → c kostet 2 + 1 + 4, der
Pfad e → h → i kostet 1+1+1. Im Folgenden nehmen Sie bitte an, dass die Abbiegekosten
von x über y nach z durch die Funktion AK(x, y, z) berechnet wird. Sie brauchen diese
Funktion nicht implementieren!
Lösen Sie nun die beiden folgenden Teilaufgaben.
a) Angenommen, der Algorithmus von Dijkstra auf Foliensatz 13, Folie 26 wird wie folgt
modifiziert: new_dist ergibt sich nicht nur aus der Distanz A[k] + w, sondern auch
die Abbiegekosten werden addiert. Genauer: Sei y der Knoten, der zuletzt die Distanz
zu k aktualisiert hat. Als Distanz A[f ] werden die Distanz zu A[k], die Kantenkosten
w der Kante (k, f ) und die Abbiegekosten des Pfads von y über k nach f summiert,
falls dies eine Verbesserung darstellt.
Zeigen Sie, dass die beschriebene Modifikation des Dijkstra-Algorithmus nicht die
korrekten kürzesten Pfade ausgehend von einem Startknoten x berechnet.
b) Beschreiben Sie detailliert, wie ein gegebener Graph G modifiziert werden kann, um
die Abbiegekosten direkt in die Kantenkosten zu integrieren, damit der unmodifizierte
Dijkstra-Algorithmus die korrekten Pfadkosten für das Szenario mit Abbiegekosten
berechnet.
2. Sei G = (V, E) ein ungerichteter, zusammenhängender Graph. Dann ist e ∈ E eine
Brückenkante, falls der Teilgraph, der durch Entfernen von e aus G entsteht, nicht mehr
zusammenhängend ist. Der unten angegebene Graph enthält genau eine solche Brückenkante e.
a) Entwerfen Sie einen Algorithmus, der für einen gegebenen Graph G = (V, E) alle
Brückenkanten berechnet. Notieren Sie Ihren Algorithmus als Pseudocode.
b) Analysieren Sie die Laufzeit Ihres Algorithmus in Abhängigkeit von |V | und |E|.
Hinweis: Es geht bei dieser Aufgabe nur darum, einen korrekten Algorithmus anzugeben; Ihr Algorithmus muss nicht optimal sein.
Herunterladen