Kapitel 8: Graphalgorithmen 8.1 8.2 8.3 8.4 8.5 8.6 Grundlagen Tiefen- und Breitensuche Prim- und Kruskal-Algorithmus Kürzeste Wege in Graphen Eulersche und Hamiltonsche Graphen Bipartite Graphen 1 8 Graphalgorithmen 8.1 Grundlagen Definitionen Gerichteter Graph (Digraph): ein geordnetes Paar G = (V,E) , wobei V = Knotenmenge (meist endliche Menge), E = Kantenmenge, eine Teilmenge von V V. (Kanten haben eine Richtung!) 2 Beispiel 4 5 V = { 1, 2, 3, 4, 5 } E = { (1,2), (2,1), (1,4), (2,4), (4,5), (5,3), (3,5), (3,3) } 1 2 3 3 Definition (2) w heißt Nachfolger von v und v Vorgänger von w, wenn (v,w) aus E ist. Loop = Kante (v,v). 4 (Ungerichteter) Graph Kanten haben keine Richtung! Formal: ein geordetes Paar G = (V,E) , wobei V = Knotenmenge (meist endliche Menge), E = Kantenmenge, eine Multimenge von zweielementigen Multimengen mit Elementen aus V ist. (Auch Mehrfachkanten erlaubt!) Ein Graph heißt schlicht, wenn er keine Mehrfachkanten und keine Loops hat. 5 Beispiel V = { 1, 2, 3, 4, 5 } E = { (1,2), (1,4), (2,4), (4,5), (5,3)} 6 Gewichteter Graph Gewichteter Graph oder Graph mit Kantenbewertung: Tripel G = (V, E, d) mit (V,E): Graph d:E {x R | x 0} Kantenbewertung 7 Beispiel 0.5 0.18 V = { 1, 2, 3, 4, 5 } E = { (1,2,2.5), (1,4,0.18), (2,4,2.5), (4,5,0.5), (5,3,0.02) } 2.5 0.02 1.66 8 Eckengrad Sei v ein Knoten in einem ungerichteten Graphen. Eckengrad g(v) := Zahl der mit v inzidierten Kanten = Zahl der Kanten, die ein Ende in v haben. Handschlaglemma: Sei G ein ungerichteter Graph ohne Loops. Dann ist {v V} g(v) eine gerade Zahl. In einem gerichteten Graphen: Eingangs- (Ausgangs-)Grad eines Knotens v := Zahl der in v endenden (beginnenden) Kanten. 9 Erreichbarkeit und Zusammenhang in Graphen In einem gerichteten oder ungerichteten Graphen: • Pfad: Knotenfolge (v0, v1, …, vp) mit (vi-1,vi) in E für i=1,…,p. • Einfacher Pfad oder Weg: Pfad, in dem alle Knoten paarweise verschieden sind außer evtl. dem ersten und letzten. • Geschlossener Weg oder Kreis oder Zyklus: Weg, in dem der erste und der letzte Knoten identisch sind. Anmerkung bei einem ungerichteten Graphen: Gibt es mehrere Kanten zwischen vi-1 und vi, so sollte man noch sagen, welche Kante gemeint ist. 10 Definitionen (3) In einem ungerichteten Graphen: • Knoten v und w sind verbunden oder w ist von v erreichbar, wenn es einen Weg von v nach w gibt. • Ein Teilgraph eines Graphen G=(V,E) ist ein Graph G´=(V´,E´) mit: V´ Teilmenge von V und E´ Teilmenge von E. • Der Teilgraph, der aus allen von w erreichbaren Ecken und allen zwischen diesen Ecken verlaufenden Kanten besteht, heißt Zusammenhangskomponente von w. • Ist dies der gesamte Graph (und das gilt dann unabhängig von w), so heißt G zusammenhängend. • Ein zusammenhängender Graph ohne Zykel heißt Baum. 11 Definitionen (4) Analog in einem gerichteten Graphen: • Zwei Knoten v und w heißen stark verbunden, wenn es einen Weg von v nach w und einen Weg von w nach v gibt. • Eine stark verbundene Komponente ist ein Teilgraph (gerichtet) mit maximaler Knotenzahl, in dem jeder Knoten mit jedem stark verbunden ist. 12 Beispiele für Graphen: • Verkehrsnetze mit Straßen und Kreuzungen. • Leitungsnetze • Einbahnverkehr oder Ströme: gerichtete Graphen zum Zuge. • Computer (Datenübertragugns-) netzwerke Längen oder Kosten wie Kapazitäten von Kanten stellen Gewichtungen, also Kantenbewertungen dar. 13 Wichtige Fragestellungen in der Theorie der Graphen • Berechnung der kürzesten Entfernungen von einem Ausgangspunkt zu allen anderen Ecken. • Berechnung eines aufspannenden Baumes (ev. mit minimalem Gesamtgewicht aller Kanten). • Suchen eines Elementes in einem Graph, Feststellung der Kreisfreiheit. • Berechnung eines Weges, der genau alle Kanten oder genau alle Ecken einmal durchläuft. • Aufteilung eines Graphen in zwei elementfremde Mengen und Untersuchung der Kanten zwischen diesen Mengen. • Färbungsprobleme bei Graphen. • Eigenschaften planarer Graphen (Graphen, die auf die Ebene gelegt werden können, ohne dass sich Kanten überschneiden) 14 Darstellungen von gerichteten oder ungerichteten Graphen 4 5 1. Hier werden die Ecken jeweils an Zeilen und Spalten angetragen und einer Kante von der i-ten zur k-ten Ecke entspricht eine 1 an der Stelle (i,k) der Matrix. 1 3 2 1 1 2 3 2 3 * 5 * * 4 5 4 * * * * * Adjazenzmatrix: Bei einem ungerichteten Graphen braucht nur eine untere bzw. obere Dreiecksmatrix notiert zu werden und es können Doppelkanten Berücksichtigung finden. 15 Adjazenzliste 2. Adjazenzliste: Es werden die Nachfolger einer Ecke in eine zur Ecke gehörige Liste eingetragen. Man erhält so ein Listen-Array oder eine Liste von Listen. 4 5 1 2 1 2 4 2 4 1 3 3 5 4 5 5 3 3 16 Inzidenzmatrix 4 3 2 5 1 6 1 1 2 1 1 2 3 3 1 4 5 1 1 1 4 1 5 1 6 2 3. Inzidenzmatrix: eines ungerichteten Graphen G=(V,E): eine |V| |E|-Matrix I, mit i(j,k) = 0, wenn die j-te Ecke nicht mit der k-ten Kante inzidiert, bzw. i(j,k)=1,2, wenn die k-te Kante ein bzw. zweimal (bei Loops) mit der j-ten Ecke inzidiert. 1 1 17 Speicheraufwand 1. Adjazenzmatrix Speicherplatz: O(|V|²) Aufwand für die Frage: „gibt es eine Kante (v,w)?“: O(1). 2. Adjazenzliste Speicherplatz: O(|V|+|E|) Aufwand für die Frage: „gibt es eine Kante (v,w)?“: O(|E|). 3. Inzidenzmatrix Speicherplatz: O(|V| • |E|) 18 8.2 Tiefen und Breitensuche Expansion eines gerichteten Graphen von einem Knoten v aus: der Baum X(v) mit: • X(v) = (v), falls v keine Nachfolger hat, • X(v) = (v, X(v1), …, X(vp)), falls v1,…,vp die Nachfolger von v sind. Wenn der Graph Zykel enthält, ist die Expansion unendlich groß. 19 4 5 Beispiel Expansion 1 2 3 20 Zwei Suchalgorithmen Tiefensuche in einem gerichteten Graphen G: PreorderDurchlauf der Expansion von G mit Abbruch jeweils bei einem schon besuchten Knoten des Graphen: rekursiv: Tiefensuche (v) wenn v noch nicht besucht, dann führe aus: Verarbeite v; Markiere v als besucht; für jeden Nachfolger N(v) von v (von links nach rechts) rufe Tiefensuche (N(v)) auf. Aufwand: O(|V| + |E|). Datenstruktur: Stack. 21 Breitensuche Breitensuche: durchlaufe in der Expansion zuerst die Wurzel, dann die Knoten auf Level 1, sodann auf Level 2, usw. Aufwand: O(|V| + |E|). Datenstruktur: Queue 22 Algorithmus Breitensuche print(root) Mark(root) p = new Queue() put all neighbors of root in q and mark them while(!empty(q)) { o = deletemin(q) print(o) put all not marked neighbors of o in q and mark them } • 23 Beispiel Tiefen- und Breitensuche 4 5 1 2 3 24 8.3 Prim- und Kruskal-Algorithmus Erinnerung: Ein zusammenhängender (ungerichteter) Graph ohne Zykel heißt Baum oder freier Baum. Eigenschaften: • Er besitzt bei n Knoten genau n-1 Kanten. • Fügt man eine beliebige Kante hinzu, so enthält der aus dem Baum entstehende Graph einen Zyklus. • Leitungsnetze können mit freien Bäumen modelliert werden. Ziel: Zu einem gegebenen zusammenhängenden gewichteten ungerichteten Graphen einen minimalen Spannbaum berechnen (einen Baum, der Teilgraph ist, alle Knoten enthält und minimales Gesamtgewicht hat). 25 Algorithmus von Kruskal Beschreibung: • Zerlege den Graphen und seine Knotenmenge in einelementige Komponenten einer Partition P einer Find-and-Merge-Struktur. • Die Kanten werden in der Reihenfolge aufsteigender Kosten betrachtet (mittels Priority-Queue Q): Verbindet eine Kante zwei getrennte Komponenten, so wird sie in den zu konstruierenden minimalen Spannbaum T aufgenommen, und die beiden Komponenten werden miteinander verschmolzen. Anderenfalls wird die Kante ignoriert. • Enthält P zum Schluss nur noch eine Komponente, so ist T minimaler Spannbaum von G. 26 C D 6 C D 6 5 5 5 A B 5 3 5 A 2 B 5 3 2 7 7 4 4 F E 3 F E 3 3 1 3 1 2 2 2 2 G G C D 6 C 5 5 A 3 5 B 5 D 6 5 A 2 B 5 3 2 7 7 4 4 F E 3 F E 3 3 1 2 3 1 2 2 G 2 G 27 C D 6 C 5 5 A 3 5 B 5 D 6 5 A 2 B 5 3 2 7 7 4 4 F E 3 F E 3 3 1 3 2 1 2 2 2 G C G D 6 5 5 A B 5 3 2 7 4 F E 3 3 1 2 2 G Sei G=(V,E,d) ein zusammenhängender gewichteter Graph mit n Ecken. Es sei e := |E|. Algorithmus Kruskal (G) //berechnet einen minimalen Spannbaum T zum Graphen Initialisiere die Partition P so, dass jeder Knoten aus V in einer eigenen Komponente ist; # O(n) Sei T = (V, { }); Bilde eine Priority-Queue (Heap) Q aus allen Kanten # O(e log e) ncomp := n; while ncomp > 1 do # maximal e Schleifendurchläufe (Q, (v,w)) := deletemin(Q); # O(log e) a:= find (P, v); # O(log n) b:= find (P, w); # O(log n) if a!=b {insert(T,(v,w)) ; P:= merge(P,a,b); dec(ncomp); } # O(1) end {Kruskal}. Partition in Find-and-Merge-Struktur: hier mit Bäumen implementiert. Gesamtaufwand: O(e log e) (Beachte: e >= n-1, da der Graph zusammenhängend ist). 29 Korrektheit des Algorithmus´ von Kruskal: Zu zeigen: der konstruierte Baum T ist ein minimaler Spannbaum. Widerspruchsbeweis. Annahme: das ist nicht der Fall. Sei H ein minimaler Spannbaum, der mit T die meisten Kanten gemeinsam hat. Sei ei die erste im Algorithmus von Kruskal betrachtete Kante, die nur in E(T) oder nur in E(H), aber nicht in beiden Kantenmengen ist. Nach dem Algorithmus von Kruskal ist der zweite Fall (in E(H), aber nicht in E(T)) unmöglich. Also ist ei in E(T), aber nicht in E(H). Fügt man ei dem Baum H hinzu, so erhält man in „H mit ei “ einen Kreis. Auf diesem Kreis liegt eine Kante em, die nicht zu T gehört. Nehmen wir sie heraus, so haben wir einen neuen Spannbaum H' gefunden, der nur höchstens so großes Kantengewicht wie H hat. Nach der Definition von ei wird nämlich ei im Algorithmus von Kruskal eher behandelt als em, hat also höchstens so großes Gewicht wie em. Damit ist H´ ebenfalls ein minimaler Spannbaum und hat eine zusätzliche gemeinsame Kante mit T im Widerspruch zur Annahme. 30 Algorithmus von Prim Sei G=(V,E,d) ein zusammenhängender gewichteter Graph. Ziel: Berechne einen minimalen Spannbaum T. Kurz: Baue den Baum T schrittweise auf: füge immer eine Kante mit einem Ende im schon erhaltenen Baum und dem anderen Ende außerhalb des Baums und mit minimalem Gewicht hinzu. Bis alle Ecken zum Baum gehören. 31 Beispiel C C D 6 D 6 5 5 5 5 A 3 A B 5 B 5 3 2 2 7 7 4 4 F E 3 F E 3 3 1 2 3 1 2 2 G 2 G 32 C C D 6 D 6 5 5 5 5 A A B 5 3 B 5 3 2 2 7 7 4 4 3 3 3 1 F E F E 3 1 2 2 2 2 G G C D 6 5 A -> B -> E -> {G->F, D, C} 5 A B 5 3 2 7 4 F E 3 3 1 2 2 G 33 Algorithmus von Prim - detaillierter Lege an: • Eckenliste der n Ecken an, in der die zu den Ecken gehörigen Kanten mit ihren Gewichten und die Heappositionen (s.u.) der Ecken angegeben sind, • Kantenliste mit den inzidenten Ecken. Initialisiere zwei Eckenmengen M = { } und N=V \ M sowie den Spannbaum T = (V, { }). Man wählt nun ein y0 aus V, setzt M:={y0} und passt N an. Nun definiert man s(y) := d(y,y0), wobei d(y) = maxInteger gilt, falls keine Kante von y0 zu dem Element y führt. Man bildet einen Heap (Größenfunktion: s(y)) aus allen Ecken aus N und speichert die Heappositionen in der Eckenliste. WHILE N <> { } DO BEGIN Sei y1 der Knoten mit minimalem s(y1). Man fügt die Kante von M nach y1 in T ein, entfernt y1 aus dem Heap und aus N und fügt y1 in M ein. Definiere neu s(y) := min {s(y), d(y1,y)} für alle y aus N. Nun muss der Heap für alle Nachbarn von y1 angepasst werden und die Heapeigenschaft wieder hergestellt werden. Das kann mittels der Information Heapposition jeweils mit einem Aufwand von O(log n) geschehen. END # While 34 Aufwand des Algorithmus von Prim: • Erstellung der Eckenliste, der Kantenliste und des ersten Heaps. Aufwand: O(|E| + |V| log |V|) (oder bei Verwendung der Idee aus Heapsort zum Aufbau des ersten Heaps: O(|E| + |V|) = O(|E|) ). • Der Update des Heaps erfordert den Durchlauf der Liste aller Nachfolger einer Ecke und ist somit vom Aufwand O(|E| log |V|), nämlich O(log |V|) pro Nachfolger. • Die Heapeigenschaft wird |V|-mal nach Entnahme des Minimums hergestellt. Aufwand: O(|V| log |V|). Gesamtaufwand: O(|E| log|V|) (denn |E| |V|-1) Anmerkung: mit einer verbesserten Heap-Datenstruktur noch schneller: O(|E| + |V| log |V|) (siehe [Ottmann, Widmayer: Algorithmen und Datenstrukturen]). 35 Grundlage für Algorithmus von Prim: Beobachtung: Minimale Spannbäume enthalten eine Kante mit Minimalgewicht. Beweis: Wäre das nicht der Fall, so könnte man diese Kante hinzufügen und erhielte dann einen Kreis. Zwei in diesem Kreis benachbarte Ecken u, w können dann über eine Kante (u,w) , aber auch noch über einen weiteren Weg miteinander verknüpft werden. Somit kann auf diesem Weg eine Kante gelöscht werden, ohne dass die Baumeigenschaft verloren geht. Damit wäre ein neuer Spannbaum erreicht, der höchstens das Gewicht des alten, also dasselbe Gewicht wie der alte haben muss. Somit enthielt der Ausgangsbaum bereits eine Kante minimalen Gewichtes. 36 Diese Eigenschaft gilt sogar noch allgemeiner. Seien U und W eine Zerlegung der Eckenmenge von G in zwei disjunkte Teilmengen und (u,w) eine Kante mit minimalen Kosten zwischen diesen beiden Zerlegungen. Dann gibt es einen minimalen Spannbaum, der diese Kante enthält. Beweis: Auch hier könnte man (u,w) gegebenenfalls hinzufügen und dafür eine andere Verbindung zwischen U und W in Form einer Kante (u',w') löschen. Die Korrektheit des Algorithmus´ von Prim zeigt man, indem man die letzte Beobachtung in jedem Schleifendurchlauf auf die Menge U der zum schon konstruierten Teilbaum gehörigen Ecken und ihr Komplement W = V \ U anwendet. 37 8.3.2 Greedy-Verfahren Greedy-Verfahren zum Finden der Lösung eines Problems beginnen mit der leeren Lösung und bauen diese in jedem Schritt mit der derzeit optimal erscheinenden Möglichkeit aus. Der Algorithmus von Prim ist ein Greedy-Verfahren: Er beginnt mit einer Ecke und fügt zum schon konstruierten Baum immer eine Kante mit dem kleinsten Gewicht hinzu (die genau ein Ende im schon konstruierten Baum hat). 38