1 topologisches Sortieren - Übung Algorithmen und Datenstrukturen

Werbung
Wolfgang Hönig / Andreas Ecke
WS 09/10
1 topologisches Sortieren
1.1 Überblick
1. Solange noch Knoten vorhanden:
a) Suche Knoten v, zu dem keine Kante führt (Falls nicht vorhanden ⇒ keine
topologische Sortierung möglich)
b) v an Ausgabeliste anhängen
c) v und alle Kanten welche von v ausgehen löschen
1.2 Grundidee
Häufig können Daten nicht eindimensional in Listen dargestellt werden, sondern es sind
kompliziertere Datenstrukturen nötig. Wenn auch Bäume nicht ausreichen, bietet sich eine
Darstellung als Graph an. Diese bestehen aus Knoten (mit einer Beschriftung) und Kanten,
welche je zwei Knoten (gegebenenfalls mit Richtung) verbinden können. Bäume (und damit
auch Listen) sind daher offenbar Spezialfälle von Graphen.
Offenbar sind auch für diese komplexeren Strukturen Sortier- und Suchalgorithmen notwendig. Im Folgenden wird daher die topologische Sortierung sowie die Tiefen- und Breitensuche erläutert.
Ein gerichteter Graph ist sehr gut geeignet, um Abhängigkeiten darzustellen. Beispielsweise
benötigt man zum Bestehen der Logikklausur Wissen über Aussagen- und Prädikatenlogik.
Die Prädikatenlogik setzt die Aussagenlogik voraus. Damit man weiß, worauf man sich
einlässt, sollte man Lord of Logic sehen und zur Vorbereitung auf die Prädikatenlogik
bietet sich hoelli.avi an. Insgesamt lässt sich dies als Graph darstellen:
Aussagenlogik
Prädikatenlogik
Klausur
Lord of Logic
hoelli.avi
Die Frage der topologischen Sortierung ist nun: In welcher Reihenfolge sollte man lernen,
um die Klausur zu bestehen? (Zum Beispiel könnte man zuerst Lord of Logic schauen,
dann die Aussagenlogik lernen, danach hoelli.avi anschauen, die Prädikatenlogik lernen
und schließlich zur Klausur gehen.)
1.3 Erklärung am Beispiel
Folgender gerichteter Graph soll topologisch sortiert werden:
5
2
6
4
1
3
Ausgabeliste:
Zuerst sucht man einen Knoten in dem Graphen, zu dem keine Kante führt. In diesem
Fall gehen von Knoten 6 und 1 je zwei Kanten aus, aber es führt keine Kante zu den
beiden Knoten. Wir können also entweder den Knoten 6 oder 1 wählen. Im folgenden Bild
1
Wolfgang Hönig / Andreas Ecke
WS 09/10
wurde Knoten 6 gewählt und deshalb an die Ausgabeliste drangehängt. Da dieser Knoten
nun komplett abgearbeitet wurde, kann er ohne weiteres inklusive der zugehörigen Kanten
gelöscht werden:
5
2
4
1
3
Ausgabeliste: 6
Jetzt wird wieder ein Knoten gesucht, zu dem keine Kanten führen (2 oder 1 erfüllen die
Bedingung). Nach dem Anhängen an die Liste und löschen von Knoten 2 ergibt sich:
5
4
1
3
Ausgabeliste: 6, 2
Diesmal fällt die Auswahl auf Knoten 3:
5
4
1
Ausgabeliste: 6, 2, 3
Jetzt bleibt nur noch Knoten 1 übrig, welcher die Eigenschaft erfüllt:
5
4
Ausgabeliste: 6, 2, 3, 1
Nun ist nur noch Knoten 4 auswählbar:
5
Ausgabeliste: 6, 2, 3, 1, 4
Es ergibt sich schließlich: 6,2,3,1,4,5.
Aus dem Text ist ersichtlich, dass die Wahl des Knotens v teilweise beliebig ist (Am Anfang
zum Beispiel 1 oder 6). Deshalb gibt es auch mehrere korrekte Lösungen:
6,2,3,1,4,5 oder 6,1,2,3,4,5 oder 6,2,1,3,4,5 oder 1,6,2,3,4,5.
1.3.1 Eigenschaften
Komplexität: O(n + m) mit n . . . Anzahl Knoten, m . . . Anzahl Kanten
2
2 Tiefensuche (DFS)
2.1 Aufgabentyp
Der gerichtete Graph G = (V, E) sei durch folgende Darstellung gegeben: [...]
Wenden Sie auf den Graphen G den DFS-Algorithmus mit dem Startknoten [...] an, und
bestimmen Sie auf diese Weise einen depth first forest. Geben Sie mindestens drei unterschiedliche Lösungen an. Zwischenschritte zu den Lösungen brauchen Sie nicht anzugeben.
2.2 Überblick
1. Startknoten v übernehmen, falls noch nicht besucht
2. für jeden nicht besuchten Nachfolger u in v
a) Kante von v nach u
b) Tiefensuche(u) (rekursiv!)
3. Falls Tiefensuche komplett beendet (inklusive rekursiver Abbau): Tiefensuche(u) für
einen noch nicht besuchten Knoten u
2.3 Grundidee
Große Graphen sind meist nicht in ihrem gesamten Ausmaß bekannt (d.h. die komplette
Menge der Knoten und Kanten ist nicht bekannt), sondern es existiert ein bekannter Knoten. Jeder Knoten wiederum kennt seine Nachfolger (also die Kanten). Zum Beispiel kann
das Internet als Graph aufgeschrieben werden, wobei nur ein Startknoten bekannt ist (der
eigene Computer) sowie einige Kanten (Nachbarcomputer). Soll nun ein bestimmter Knoten gesucht werden, muss natürlich verhindert werden, dass ein Knoten mehrmals besucht
wird. Ansonsten könnte die Suchanfrage durch Zyklen in dem Graphen ewig andauern.
Ziel einer Suche auf Graphen ist also primär doppeltes Besuchen von Knoten zu vermeiden. Letztendlich gibt es zwei verschiedene Strategien: Tiefen- und Breitensuche. Bei der
Tiefensuche wird ein Nachfolgerknoten bevorzugt behandelt, während bei der Breitensuche (nahezu) alle Nachfolger gleichmäßig abgearbeitet werden. Die eigentliche Suche rückt
bei uns etwas in den Hintergrund - es wird jeweils nur der Suchbaum (bzw. Suchwald)
betrachtet. Das ist die Datenstruktur, welche im worst-case (Element nicht gefunden) entstehen würde. Um eine vollständige Suche herzustellen ist nur bei jedem neuen Element
ein jeweiliger Vergleich nötig. Prüfungsrelevant ist jedoch nur der Wald, welcher bei der
Tiefensuche auf gerichteten Graphen entsteht, beziehungsweise der Baum, welcher bei der
Breitensuche auf ungerichteten Graphen entsteht.
2.4 Erklärung am Beispiel
Folgender gerichteter Graph sei gegeben:
3
Wolfgang Hönig / Andreas Ecke
6
5
3
1
WS 09/10
4
7
2
Als erstes kann ein Startknoten ausgesucht werden (in der Klausur ist dieser meist vorgegeben). Hier soll Knoten 1 gewählt werden. Dieser hat die Nachfolgerknoten 2, 4 und 6.
Alle drei Nachfolger wurden noch nicht besucht. Also wählen wir einen Nachfolger - z.B.
die 6 - aus, ziehen eine Kante von 1 nach 6, rufen Tiefensuche(6) auf und erhalten:
1
6
5
6
3
1
4
7
2
Die 6 hat keinen Nachfolger, sodass kein weiterer rekursiver Aufruf erfolgen kann. Also
betrachten wir wieder eine Ebene obendrüber den Knoten 1. Dieser hat jetzt noch zwei
nicht besuchte Nachfolger: 2 und 4. Wir wählen wiederum einen Nachfolger aus (hier die
2), ziehen eine Kante von 1 nach 2 und rufen Tiefensuche(2) auf. Damit ergibt sich:
1
6
5
6
3
1
4
2
7
2
Der Knoten 2 hat nur einen noch nicht besuchten Nachfolger: die 7. Damit erhalten wir:
1
6
5
6
3
1
4
2
7
7
2
Die 7 hat wiederum nur einen Nachfolger (die 6). Allerdings wurde diese schon besucht,
sodass kein rekursiver Aufruf erfolgen kann. Wir betrachten also wieder eine Ebene niedriger: den Knoten 2. Auch dieser Knoten hat keine noch nicht besuchten Nachfolger, so dass
wiederm eine Ebene tiefer betrachtet wird: die 1. Hier ist noch ein nicht besuchter Knoten
übrig, nämlich die 4, sodass sich ergibt:
1
6
5
6
3
1
4
2
4
7
7
2
Die 4 hat keine noch nicht besuchten Nachfolger. Also betrachten wir wieder die nächsthöhere Ebene. Auch die 1 hat keine noch nicht besuchten Nachfolger mehr. Also muss
die Tiefensuche noch einmal für einen anderen Startknoten aufgerufen werden. In diesem
Beispiel bietet sich die 5 an:
1
6
6
3
1
4
5
5
2
4
7
2
7
4
Wolfgang Hönig / Andreas Ecke
WS 09/10
Der Knoten 5 hat nur einen noch nicht besuchten Nachfolger: die 3:
1
6
6
3
1
5
5
2
4
3
7
4
7
2
Die 3 hat keine nicht besuchten Knoten mehr, ebenso wenig wie die Ebene obendrüber
(Knoten 5). Also wurden alle Knoten mit der Tiefensuche erschlossen und der Algorithmus
ist fertig.
In der Klausur ist Baum und kein gerichteter Graph gefordert. Damit ergibt sich als korrekte Schreibweise:
5
1
3
6
2
4
7
Auch hier sind wieder sehr viele verschiedene Lösungen möglich, je nachdem wie die Knotenreihenfolge gewählt wird. Bei Startknoten 1 ist zum Beispiel auch folgende Lösungen
5
3
5
korrekt:
1
oder
1
3
6 4
6 4
2
2
7
7
2.4.1 Eigenschaften
Komplexität: O(n + m) mit n . . . Anzahl Knoten, m . . . Anzahl Kanten
5
3 Breitensuche (BFS)
3.1 Aufgabentyp
Der ungerichtete Graph G = (V, E) sei durch folgende Darstellung gegeben: [...]
Wenden Sie auf den Graphen G den BFS-Algorithmus mit dem Startknoten [...] an, und
bestimmen Sie auf diese Weise einen breadth first tree. Geben Sie mindestens drei unterschiedliche Lösungen an. Zwischenschritte zu den Lösungen brauchen Sie nicht anzugeben.
3.2 Überblick
1. Startknoten an Warteschlänge hängen
2. Solange Warteschlage nicht leer: Element v aus der Warteschlage entnehmen und für
jeden nicht besuchten Nachfolger u von v:
• Kante von v nach u
• u an Warteschlange hängen
3.3 Grundidee
siehe Tiefensuche.
3.4 Erklärung am Beispiel
Folgender ungerichteter Graph sei gegeben:
6
5
3
1
4
7
2
Warteschlange: 1
Als erstes kann ein Startknoten ausgesucht werden (in der Klausur ist dieser meist vorgegeben). Hier soll Knoten 1 gewählt werden. Also wird die 1 an unsere Warteschlange W = 1
gehängt. Jetzt wird das erste Element aus der Warteschlange entnommen, in diesem Fall
also die 1. Knoten 1 hat die Nachfolgerknoten 6, 3, 4 und 2. Alle vier Nachfolger wurden
noch nicht besucht. Also zeichnen wir eine Kante von 1 nach 2, 1 nach 3, 1 nach 4 und 1
nach 6. Und hängen diese Nachfolgerknoten an die Warteschlange:
1
6
5
3
1
4
7
6
3
4
2
2
Warteschlange: 6,3,4,2
Jetzt wird wieder das erste Element - diesmal die 6 - aus der Schlange entnommen. Es gibt
nur einen noch nicht besuchten Nachfolgerknoten: Knoten 7. Also zeichnen wir eine Kante
von 6 nach 7 und fügen die 7 der Warteschlange hinzu:
6
Wolfgang Hönig / Andreas Ecke
1
6
5
3
1
WS 09/10
7
4
6
3
4
2
2
7
Warteschlange: 3,4,2,7
Das nächste Element, welches entnommen wird ist die 3. Der einzige Nachfolgerknoten ist
die 5, welche an die Warteschlange gehängt wird. Zustätzlich entsteht die neue Kante von
3 nach 5:
1
6
5
3
1
7
4
6
3
7
5
4
2
2
Warteschlange: 4,2,7,5
Nun müssen der Reihe nach noch die 4,2,7 und 5 abgearbeitet werden. Da aber keiner
dieser Knoten noch nicht besuchte Nachfolger hat, ist die Breitensuche beendet.
In der Klausur ist ein Baum und kein Graph gefordert. Damit ergibt sich als korrekte
Schreibweise:
1
6
3
7
5
4
2
Auch hier sind wieder sehr viele verschiedene Lösungen möglich, je nachdem wie die Knotenreihenfolge gewählt wird. Bei Startknoten 1 ist zum Beispiel auch folgende Lösung
korrekt:
1
2
7
6
3
4
5
Mit Startknoten 5 ergibt sich:
5
3
6
1
oder
5
7
4
2
7
6
3
2
1
4
3.4.1 Eigenschaften
Komplexität: O(n + m) mit n . . . Anzahl Knoten, m . . . Anzahl Kanten
7
Herunterladen