Algorithmen und Komplexität Lösungsvorschlag zu

Werbung
ETH Zürich
Institut für Theoretische Informatik
Prof. Dr. Angelika Steger
Florian Meier, Ralph Keusch
HS 2017
Algorithmen und Komplexität
Lösungsvorschlag zu Übungsblatt 3
Lösungsvorschlag zu Aufgabe 1
Ein Graph ist genau dann bipartit, wenn seine Knoten so mit zwei Farben (rot und blau) färbbar
sind, dass keine zwei adjazenten Knoten die gleiche Farbe erhalten. Eine solche 2-Färbung nennen wir zulässig; die Menge der roten Knoten und die Menge der blauen Knoten ist dann gerade
die disjunkte Zerlegung V1 ] V2 der Knotenmenge, die in der Definition von „bipartit“ verlangt
wird. Beachte, dass so eine Zerlegung in einem zusammenhängenden Graphen eindeutig ist (bis
aufs Vertauschen der beiden Mengen). Ist ein Knoten gefärbt müssen alle seine Nachbaren die
andere Farbe besitzen und deren Nachbaren wieder die andere Farbe, usw.
Wir nehmen zunächst an, dass der Graph zusammenhängend ist. Wir führen ausgehend von
einem beliebigen Startknoten eine Breitensuche durch, und färben den Graphen während der
Breitensuche ein. Den Startknoten färben wir o.B.d.A. mit der Farbe rot ein. Von nun an haben
wir keine Wahl mehr für die Farbe: Wenn wir von einem roten Knoten aus einen Knoten besuchen,
müssen wir ihn blau färben, und umgekehrt.
Stossen wir also im Laufe des Algorithmus auf einen “Widerspruch”, d.h. wir besuchen z.B. einen
bereits rot eingefärbten Knoten von einem ebenfalls rot gefärbten Knoten aus, dann gibt es keine
zulässige 2-Färbung, da wir nie die Wahl gehabt hatten irgendeinen Knoten anders zu färben.
Wir können die Breitensuche abbrechen mit der Meldung, dass der Graph nicht bipartit ist.
Können wir dagegen die Breitensuche komplett durchführen, ohne dass es ein solches Problem
gibt, haben wir eine zulässige 2-Färbung gefunden, da wir im Verlauf der Breitensuche für jede
Kante überprüft haben, ob sie zwei Knoten verschiedener Farbe verbindet. Der Graph ist also
bipartit.
Ein Graph ist genau dann bipartit, wenn alle seine Zusammenhangskomponenten bipartit sind.
Ist G nicht zusammenhängend, können wir deshalb für jede der Zusammenhangskomponenten
einzeln testen, ob sie bipartit ist. Die Laufzeit unseres Algorithmus ist in jedem Fall O(|V | + | E|)
(Breitensuche!).
Lösungsvorschlag zu Aufgabe 2
Wir beobachten zunächst, dass ein Digraph D = (V, A) keine topologische Ordnung besitzen
kann, wenn er Zyklen (d.h. gerichtete Kreise) enthält. Ausserdem beobachten wir, dass jeder zyklusfreie Digraph mindestens einen Knoten ohne ausgehende Kanten enthält. Wir nennen solche
Knoten Senken und stellen folgende Behauptung auf.
Lemma 1. Sei D = (V, A) ein Digraph und v ∈ V eine Senke in D. Eine topologische Ordnung von
D \ {v}, f : V \ {v} → {1, ..., |V | − 1} bildet zusammen mit f (v) := |V | eine topologische Ordnung
von D.
Beweis. Wir nehmen an, dass es eine Kante (u, w) ∈ A gibt, so dass f (u) > f (w) gilt. Weil f auf
V \ {v} eine topologische Ordnung bildet, muss w = v sein. Dies führt aber zum Widerspruch,
weil per Definition für alle u ∈ V \ {v} gilt f (u) < f (v) = |V |.
1
Falls D keinen Zyklus enthält können wir also, indem wir sukzessive Senken entfernen und ihnen
den grössten verbleibenden Wert zuordnen, eine topologische Ordnung finden.
Wir wollen nun diese Idee verwenden um mittels modifizierter Tiefensuche eine topologische
Ordnung zu finden. Wir stellen den Status jedes Knoten durch eine Farbe dar: Schwarze Knoten
v wurde bereits abgearbeitet, d.h. ihnen wurde bereits ein Wert f [v] zugewiesen. Graue Knoten
sind gerade auf dem Stack der Tiefensuche, und weisse Knoten wurden noch nicht betrachtet.
Einige Bemerkungen zum Pseudocode:
• Mit Γ(v) sind jeweils nur die durch von v ausgehende Kanten erreichbaren Knoten gemeint.
• Sind wir an einer Senke v angekommen, so ist Γ(v) leer. Dann kann v keinen durch ausgehende Kante erreichbaren Nachbarn haben, der nicht schwarz wäre, und der Knoten v wird
innerhalb der dritten if-Schleife schwarz gefärbt.
• Die äussere for-Schleife garantiert, dass die Tiefensuche den ganzen Graphen abarbeitet
und der Algorithmus nicht terminiert, bevor allen Knoten ein Wert f [v] zugewiesen wurde.
Zur Korrektheit: Es ist klar, dass wir im Lauf einer Tiefensuche früher oder später entweder auf
eine Senke des weiss-grauen Restgraphen oder einen bereits besuchten (also grauen) Knoten
stossen. Im ersten Fall können wir diesem Knoten wie beschrieben einen Wert zuweisen, im
zweiten Fall haben wir einen Zyklus gefunden, d.h. einen Beweis, dass es keine topologische
Sortierung von G gibt. Wenn der zweite Fall nie eintritt, sind die Knoten zum Schluss topologisch
geordnet. Es ist einfach zu sehen, dass für alle Kanten (u, v) gelten muss, dass f (u) ≤ f (v). Falls
f (u) > f (v) dann würde das bedeuten, dass u vor v einen Wert zugewiesen bekommen hätte.
Dies ist aber nicht möglich, weil dann v als Nachbar von u zu diesem Zeitpunkt schwarz gefärbt
wäre, was zu einem Widerspruch führt.
Zur Laufzeit: Die äussere for-Schlaufe geht genau einmal durch alle Knoten. Die Laufzeit von
T OP S ORT ergibt sich daher aus derjenigen der Tiefensuche, ist also O(|V | + | E|).
Lösungsvorschlag zu Aufgabe 3
Sei L die Menge der Blätter des Baums T = (V, E). Es bezeichne T [V \ L] den Graphen, der
entsteht, wenn man von T die Blätter entfernt.
Lemma 2. Die Zentren von T und T [V \ L] sind für |V | ≥ 3 identisch.
Beweis. Für einen Knoten v ∈ V \ L gilt: Jeder Knoten u mit d(v, u) = ecc(v) ist ein Blatt, da es
sonst einen Knoten u0 mit d(v, u0 ) > d(v, u) gäbe. Daraus folgt, dass sich die Exzentrizität von v
um mindestens 1 verringert, wenn alle Blätter von T entfernt werden. Andererseits ist klar, dass
der Nachbar eines Blatts u mit d(v, u) = ecc(v) immer noch in T [V \ L] ist und sich deshalb die
Exzentrizität von v um höchstens 1 verringert. Es gilt also für alle Knoten v ∈ V \ L
ecc T [V \ L] (v) = ecc T (v) − 1.
Es ist klar, dass für |V | ≥ 3 die Zentren von T keine Blätter sind. Da die Zentren von T bzw.
T [V \ L] als Knoten minimaler Exzentrizität bezüglich T bzw. T [V \ L] definiert ist und sich diese
Grössen um genau 1 unterscheiden, sind die Zentren von T [V \ L] genau die Zentren von T.
Aus obigem Lemma finden wir folgendes iterative Vorgehen, um das Zentrum von T zu bestimmen: Wir entfernen in jeder Phase jeweils alle Blätter von T, d.h. sei Ti der Baum zu Beginn der
i-ten Iteration, dann wählen wir Ti+1 = Ti [V ( Ti ) \ L( Ti )]. Wir brechen die Iteration ab, sobald
|V ( Tk )| ≤ 2 erreicht ist. V ( Tk ) enthält dann genau die Zentren von T.
Die eleganteste Art, diese Idee in einen Algorithmus F IND C ENTER (siehe Pseudocode) umzusetzen, ist, die Blätter in einer Queue zu führen. Wir nehmen an, dass T als Adjazenzlisten gegeben
ist und bezeichnen mit T 0 den schrittweise zurechtgestutzten Baum. Während des Algorithmus
2
Algorithm 1 T OP S ORT (Graph G = (V, E))
for all v ∈ V do
C OLOR [v] := W HITE
end for
S := new Stack
T IME := |V |
for all w ∈ V do
if C OLOR [w] = W HITE then
v := w
repeat
if ∃u ∈ Γ(v) mit C OLOR [u] = G REY then
return “topological sort doesn’t exist”
end if
if ∃u ∈ Γ(v) mit C OLOR [u] = W HITE then
C OLOR [v] := G REY
S.Push(v)
v := u
end if
if ∀u ∈ Γ(v) : C OLOR [u] = B LACK then
C OLOR [v] := B LACK
f [v] := T IME
T IME := T IME − 1
v := S.Pop()
end if
until v = nil
end if
end for
Gib (v, f [v]) für alle v ∈ V aus
lassen wir die Adjazenzlisten von T unverändert (!), zählen aber für jeden Knoten v ∈ V seinen Grad in T 0 in der Variable D EGREE [v] mit. In jedem Durchlauf der while-Schleife wird der
vorderste Knoten v der Queue gedanklich aus T 0 entfernt und die Grade seiner Nachbarn entsprechend aktualisiert. (Natürlich hat v als Blatt höchstens einen Nachbarn in T 0 , aber da wir nur
die Adjazenzlisten von T haben, müssen wir alle Nachbarn in T durchlaufen, obwohl uns nur
einer davon wirklich interessiert!) Sobald ein Nachbar u von v ein Blatt in T 0 wird, wird er in
die Queue eingefügt. Nach jeder Iteration sind also die inneren Knoten von T 0 genau die Knoten
mit D EGREE [v] ≥ 2 und die Blätter von T 0 genau die Knoten in der Queue. Man beachte, dass
die Variable D EGREE [v] im Allgemeinen ins Negative heruntergezählt wird - dies tut nichts zur
Sache, da wir ja jeweils nur testen, ob sie genau 1 ist oder nicht, um v im richtigen Moment in die
Queue einzufügen.
Zur Korrektheit: Man überlegt sich leicht, dass der Algorithmus zuerst alle Blätter von T0 = T
entfernt, dann diejenigen von T1 = T [V \ L] usw., so dass der letzte Knoten in der Queue genau
ein Zentrum ist.
Zur Laufzeit: Die while-Schleife wird |V |-mal ausgeführt. Die darin enthaltene for all-Anweisung
wird total höchstens ∑v∈V deg(v) = 2| E|-mal ausgeführt. 2| E| ist auch der Zeitbedarf für die Initialisierung des Arrays D EGREE zu Beginn. Es ergibt sich eine Laufzeit von O(|V | + | E|). (Das
ist im Wesentlichen das Argument, mit dem in der Vorlesung und im Skript die Laufzeit der
Breitensuche begründet wird.)
Da für Bäume zudem | E| = |V | − 1 gilt, ist die Laufzeit von F IND C ENTER O(|V |).
3
Algorithm 2 F IND C ENTER (Tree T = (V, E))
Q := new Queue
for all v ∈ V do
D EGREE [v] := deg v
if D EGREE [v] = 1 then
Q.Insert(v)
end if
end for
while Q.NotEmpty do
v := Q.Dequeue()
for all u ∈ Γ(v) do
D EGREE [u] := D EGREE [u] − 1
if D EGREE [u] = 1 then
Q.Insert(u)
end if
end for
end while
return v
4
Herunterladen