Algorithmen und Datenstrukturen (für ET/IT) Sommersemester 2016 Dr. Tobias Lasser Computer Aided Medical Procedures Technische Universität München Wdh: Traversierung von Binärbäumen Sei G = (V , E ) Binärbaum. In welcher Reihenfolge durchläuft man G ? • Wurzel zuerst • danach linker oder rechter Kind-Knoten l bzw. r ? • falls l: danach Kind-Knoten von l oder zuerst r ? • falls r : danach Kind-Knoten von r oder zuerst l? w l r ! falls zuerst in die Tiefe: Depth-first search (DFS) ! falls zuerst in die Breite: Breadth-first search (BFS) 2 Wdh: Implementierung DFS Traversierung Datenstruktur: Knoten: • Algorithmus preorder: preorder(knoten): if (knoten == null) return; besuche(knoten); preorder(knoten.links); preorder(knoten.rechts); links Wert rechts • Algorithmus postorder: postorder(knoten): if (knoten == null) return; postorder(knoten.links); postorder(knoten.rechts); besuche(knoten); • Algorithmus inorder: inorder(knoten): if (knoten == null) return; inorder(knoten.links); besuche(knoten); inorder(knoten.rechts); • rekursive Algorithmen • auf Call-Stack basiert 3 Wdh: Implementierung DFS Traversierung ohne Rekursion • Datenstruktur: Knoten: links Wert rechts besucht • Hilfsmittel: Stack von Knoten “knotenStack” postorderIterativ(wurzelKnoten): knotenStack.push(wurzelKnoten); while ( !knotenStack.isEmpty() ) { knoten = knotenStack.top(); if ( (knoten.links != null) && (!knoten.links.besucht) ) knotenStack.push(knoten.links); else if ( (knoten.rechts != null) && (!knoten.rechts.besucht) ) knotenStack.push(knoten.rechts); else { besuche(knoten); knoten.besucht = true; knotenStack.pop(); } } 4 Wdh: Implementierung BFS Traversierung • Datenstruktur: Knoten: links Wert rechts • Hilfsmittel: Queue von Knoten “knotenQueue” breitensuche(wurzelKnoten): knotenQueue = leer; knotenQueue.enqueue(wurzelKnoten); while ( !knotenQueue.isEmpty() ) { knoten = knotenQueue.dequeue(); besuche(knoten); if (knoten.links != null) knotenQueue.enqueue(knoten.links); if (knoten.rechts != null) knotenQueue.enqueue(knoten.rechts); } 5 Wdh: Anwendung: Quadtree I • 4-närer Baum heißt Quadtree Quadtree Bild 0 1 1 2 3 5 6 7 8 5 7 9 10 11 2 4 9 10 11 12 4 8 12 6 Wdh: Anwendung: Quadtree II • Codierung des Baumes mit Binärzi↵ern Quadtree Bild 0 1 1 1 2 3 4 0 0 1 0 5 5 6 7 8 0 1 0 0 10 11 12 0 0 0 0 9 10 11 12 7 9 2 4 8 1001010000 0 00 7 Programm heute 7 Fortgeschrittene Datenstrukturen 8 Such-Algorithmen 9 Graph-Algorithmen Tiefensuche Breitensuche 8 Bild-Repräsentation mit Pixeln • Bild als zwei-dimensionales Feld • einzelnes Element: Pixel (picture element) • hier: jedes Pixel hat einen Grauwert zugewiesen • z.B. von 0 bis 255 9 Segmentierung von Bildern • Segmentierung: Zusammenfassung von inhaltlich zusammenhängenden Regionen gemäß eines Kriteriums • Beispiel: Segmentierung von Knochen oder Organen 10 Binäre Bilder • Binäres Bild: jedes Pixel hat entweder Wert 0 oder 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 1 1 0 0 0 1 0 0 1 1 1 1 1 0 1 1 1 0 0 1 1 1 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 • Segmentierungs-Problem: identifiziere die beiden “Sterne” ! Algorithmus? 11 Problem: Segmentierung von “Sternen” 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 1 1 0 0 0 1 0 0 1 1 1 1 1 0 1 1 1 0 0 1 1 1 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 • Gegeben: binäres Bild mit 0,1 Werten • Gesucht: Regionen im Bild mit nur 1 Werten • Ansatz: • fange an bei Pixel mit Wert 1 • wiederhole: inspiziere unbesuchte Nachbar-Pixel auf Wert 1 • beende falls kein weiterer Nachbar mit Wert 1 existiert 12 Nachbarschaft von Pixeln N N P N N 4-Nachbarschaft N N N N P N N N N 8-Nachbarschaft • 4-Nachbarschaft von Pixel P: alle direkten Nachbar-Pixel, die Kante mit P gemeinsam haben • auch genannt: Von-Neumann-Nachbarschaft • 8-Nachbarschaft von Pixel P: alle direkten Nachbar-Pixel, die Kante oder Ecke mit P gemeinsam haben • auch genannt: Moore-Nachbarschaft 13 “Stern” als Graph 1 5 2 3 4 6 7 8 10 11 12 9 13 Segmentierungs-Ansatz von linkem “Stern”: 1 starte mit Pixel 1 2 besuche Nachbarn (Schema: oben, unten, links, rechts) 14 Segmentierungs-Durchlauf als Baum 1 1 5 3 2 3 4 6 7 8 10 11 12 7 11 9 13 10 12 6 8 13 2 5 4 9 Ansatz auch genannt: Tiefensuche bzw. Depth-First Search (DFS) 15 Tiefensuche (DFS) Sei G = (V , E ) Graph (gerichtet oder ungerichtet). • Graph repräsentiert mit Adjazenzliste adj • jeder Knoten hat Farb-Markierung farbe: • weiss = noch nicht besucht • grau = besucht, gerade in Bearbeitung • schwarz = besucht, fertig bearbeitet • jeder Knoten hat Vorgänger in Besuch-Reihenfolge pred • (optional) zusätzliche Knoten-Markierungen d und f • Zeitmarke, wann Knoten entdeckt: d (discovered) • Zeitmarke, wann Knoten fertig bearbeitet: f (finalized) 16 Algorithmus DFS Input: Graph G = (V , E ) Output: Vorgänger-Liste pred, Markierungen d, f DFS(G ): for each (Knoten v 2 V ) { farbe[v ] = weiss; pred[v ] = null; } zeit = 0; for each (Knoten v 2 V ) { if (farbe[v ] == weiss) DFSvisit(v ); } DFSvisit(v ): farbe[v ] = grau; // v war weiss zeit = zeit + 1; d[v ] = zeit; for each (Knoten u 2 adj[v ]) { if (farbe[u] == weiss) { pred[u] = v; DFSvisit(u); } } farbe[v ] = schwarz; // v ist fertig zeit = zeit + 1; f[v ] = zeit; 17 DFS: Beispiel-Ablauf 1 u v w x y z • Knoten u, v, w, x, y, z • farbe direkt im Knoten markiert • d/f im Knoten notiert • pred markiert durch rote Kanten 18 DFS: Beispiel-Ablauf 2 u v w u 1/_ v 1/_ w u 2/_ v 1/_ w 2/_ 3/_ x y u v 1/_ z x w u 2/_ y v 1/_ z x w u 2/_ y v 1/_ B 4/_ 3/_ x y u v 1/_ x w u 2/_ v 1/_ 4/5 z x w u 2/7 3/6 y x 3/6 y x w 2/7 B 4/5 z z v F 4/5 z 3/_ y 1/_ B 4/5 x 3/_ y B w 2/_ B 4/_ z z 3/6 y z 19 DFS: Beispiel-Ablauf 3 u v 1/_ F F z v 1/8 4/5 u z v 1/8 F F 3/6 y z 10/ 11 B 10/ _ 9/_ C B 4/5 x w 2/7 3/6 y z 10/ _ B w 9/ 12 2/7 C B 4/5 x z v 1/8 C 4/5 y u 9/_ F 3/6 z v 1/8 C B 3/6 y u 9/_ 9/_ B x w 2/7 x w 2/7 B v 4/5 y z w 2/7 4/5 y F 3/6 x F 1/8 C B v 1/8 3/6 u 9/_ u B x w 2/7 w 2/7 4/5 y u v 1/8 3/6 x F u B 4/5 x w 2/7 3/6 y z 10/ 11 B 20 Spannwald mit DFS Sei G = (V , E ) Graph. • Spannbaum: Teilgraph G 0 = (V , E 0 ), der ein Baum ist und alle Knoten von G enthält. • existiert nur für zusammenhängende Graphen • Spannwald: Teilgraph G 0 = (V , E 0 ), dessen Zusammenhangskomponenten jeweils Spannbäume der Zusammenhangskomponenten von G sind 21 Spannwald mit DFS Sei G = (V , E ) Graph. • Spannbaum: Teilgraph G 0 = (V , E 0 ), der ein Baum ist und alle Knoten von G enthält. • existiert nur für zusammenhängende Graphen • Spannwald: Teilgraph G 0 = (V , E 0 ), dessen Zusammenhangskomponenten jeweils Spannbäume der Zusammenhangskomponenten von G sind • DFS erzeugt Spannwald • Knoten sind V , Kanten E 0 ergeben sich aus pred • im Beispiel-Ablauf: Knoten V sind schwarz, Kanten E 0 sind rot • DFS erlaubt Markierung spezieller Kanten: • Rückkanten im Spannwald (markiert mit B) • Vorwärtskanten im Spannwald (markiert mit F) • Cross-Kanten im Spannnwald (markiert mit C) 21 Komplexität von DFS • DFS erste Schleife wird |V | mal durchlaufen: ⇥(|V |) • DFSvisit wird für jeden Knoten genau einmal aufgerufen (zweite Schleife in DFS, rekursiver Aufruf in DFSvisit) • innere Schleife in DFSvisit wird |adj(v )| mal aufgerufen X v 2V |adj(v )| = ⇥(|E |) • Gesamtlaufzeit: ⇥(|V | + |E |) • Implementierung gleicher Komplexität auch nicht-rekursiv möglich (mit Stack) 22 Anwendungen von DFS • Test auf Zusammenhang von Graphen • rufe DFSvisit nur für einen Knoten auf (statt für alle) • falls nicht alle Knoten dadurch besucht: nicht zusammenhängend! • Test auf Zyklenfreiheit in Graphen • Zyklus entdeckt falls Rückkante B gefunden • Rückkante B: in DFSvisit Schleife ist Zielknoten bereits grau 23 Beispiel DFS 1 2/_ 1/_ 1/_ 2/_ 1/_ 2/_ 3/_ 2/_ 1/_ 1/_ 4/5 3/_ 4/_ 3/_ 2/_ 1/_ 4/5 3/6 24 Beispiel DFS 2 2/_ 4/5 1/_ 3/6 2/_ F 1/_ 4/5 3/6 8/_ 2/7 F 1/_ 4/5 3/6 2/7 1/_ 9/_ 10/ _ 8/_ 2/7 4/5 3/6 9/_ 8/_ 1/_ F F 4/5 3/6 2/7 1/_ F 4/5 3/6 25 Beispiel DFS 3 9/_ 9/_ 10/ _ 8/_ 2/7 1/_ 4/5 3/6 1/_ 10/ _ F 2/7 1/_ C 4/5 B 10/ 11 F 2/7 1/_ 9/ 12 1/_ 3/6 8/_ 3/6 C 4/5 3/6 9/ 12 B 2/7 4/5 9/_ B 8/_ 10/ _ F 2/7 9/_ 8/_ B 8/_ F 10/ 11 F 3/6 B 8/ 13 C 4/5 2/7 1/_ 10/ 11 F C 4/5 3/6 26 Beispiel DFS 4 9/ 12 9/ 12 B 8/ 13 10/ 11 F 2/7 1/_ 2/7 15/ _ B 1/ 14 4/5 2/7 10/ 11 F 16/ 17 C 3/6 F 16/ _ C 2/7 1/ 14 15/ _ B 8/ 13 4/5 4/5 3/6 9/ 12 15/ _ B 10/ 11 1/ 14 9/ 12 8/ 13 15/ _ B 2/7 3/6 4/5 3/6 8/ 13 C C 9/ 12 10/ 11 F 2/7 10/ 11 F 1/ 14 9/ 12 1/ 14 4/5 3/6 8/ 13 B 8/ 13 C 10/ 11 F 16/ 17 C 3/6 4/5 18/ _ 27 Beispiel DFS 5 9/ 12 15/ _ B 8/ 13 10/ 11 F 2/7 1/ 14 16/ 17 C 2/7 1/ 14 15/ _ B C 3/6 16/ 17 C 16/ 17 8/ 13 18/ 19 1/ 14 4/5 C 18/ _ 15/ 20 B 2/7 C 4/5 3/6 9/ 12 10/ 11 F 10/ 11 F 2/7 3/6 8/ 13 15/ _ B 8/ 13 4/5 18/ _ 9/ 12 1/ 14 9/ 12 10/ 11 F 16/ 17 C 3/6 4/5 C 18/ 19 28 Programm heute 7 Fortgeschrittene Datenstrukturen 8 Such-Algorithmen 9 Graph-Algorithmen Tiefensuche Breitensuche 29 Breitensuche im “Stern”-Beispiel • Segmentierung von “Sternen” 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 1 1 0 0 0 1 0 0 1 1 1 1 1 0 1 1 1 0 0 1 1 1 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 • neuer Ansatz: • anstatt direkt nächsten Nachbar ansteuern, erst alle aktuellen Nachbarn abarbeiten ! Breitensuche 30 Breitensuche im “Stern”-Beispiel: Ablauf 1 5 2 3 4 6 7 8 10 11 12 9 13 1 starte mit Pixel 1 2 besuche alle Nachbarn von aktuellem Pixel (Schema: oben, unten, links, rechts) 3 arbeite die Nachbarn der Nachbarn in selber Reihenfolge ab 31 Breitensuche im “Stern”-Beispiel: als Baum 1 1 5 3 2 3 4 6 7 8 10 11 12 7 9 11 13 10 12 2 6 8 5 9 4 13 Ansatz: Breitensuche bzw. Breadth-First Search (BFS) 32 Breitensuche (BFS) Sei G = (V , E ) zusammenhängender Graph (gerichtet oder ungerichtet). • Startknoten s 2 V • Graph repräsentiert mit Adjazenzliste adj • jeder Knoten hat Farb-Markierung farbe: • weiss = noch nicht besucht • grau = besucht, gerade in Bearbeitung • schwarz = besucht, fertig bearbeitet • jeder Knoten hat Vorgänger in Besuch-Reihenfolge pred • (optional) zusätzliche Knoten-Markierung d • Distanz (in Anzahl von Kanten) von Startknoten s • Hilfsmittel: Queue Q 33 Algorithmus BFS Input: Graph G = (V , E ), Startknoten s 2 V Output: Vorgänger-Liste pred, Markierung d BFS(G , s): for each (Knoten v 2 V ) { // Initialisierung farbe[v ] = weiss; pred[v ] = null; d[v ] = 1; } farbe[s] = grau; d[s] = 0; Q = initialize(); Q.enqueue(s); while ( !Q.isEmpty() ) { u = Q.dequeue(); for each (v 2 adj[u]) { // besuche alle Nachbarn if (farbe[v ] == weiss) { farbe[v ] = grau; d[v ] = d[u] + 1; pred[v ] = u; Q.enqueue(v ); } } farbe[u] = schwarz; // u ist erledigt } 34 BFS: Beispiel-Ablauf 1 r s t u v w x y • Knoten r, s, t, u, v, w, x, y • farbe direkt im Knoten markiert • d im Knoten notiert • pred markiert durch rote Kanten • Startknoten s 35 BFS: Beispiel-Ablauf 2 r s ∞ t 0 u ∞ r ∞ Q: ∞ v ∞ w r s 1 ∞ x ∞ ∞ v u 2 ∞ v 1 w r s 1 2 x v 1 w 2 v y v 2 x v Q: v u y ∞ t u 0 2 3 2 1 2 3 x t y 1 w Q: ∞ u ∞ r u x s w ∞ 2 1 w r 3 x t 0 Q: y x u 2 x t v Q: 2 r ∞ 2 ∞ ∞ x s 1 y t 0 u ∞ 1 w r ∞ Q: t 0 s y t 0 s 1 y 36 BFS: Beispiel-Ablauf 3 r s 1 t 0 u 2 r 3 Q: 2 v 1 w r s 1 2 x 2 2 1 w r 3 2 x s 1 t 0 u y 3 y u 2 3 y Q: 3 y u 2 Q: 3 2 x t 0 y v u 2 1 w u 3 Q: v v y t 0 s 1 2 v 1 w 2 x 3 y 37 BFS Eigenschaften Sei G = (V , E ) Graph. • BFS erzeugt Spannbaum G 0 = (V , E 0 ) • Knoten sind V , Kanten E 0 ergeben sich aus pred • im Beispiel-Ablauf: Knoten V sind schwarz, Kanten E 0 sind rot • ausgehend von Startknoten s wird nur Zusammenhangskomponente von Graph durchsucht! • falls G nicht zusammenhängend, wird kein Spannwald berechnet! • falls Stack statt Queue in BFS: DFS-artiger Algorithmus • Unterschied: nur in Zusammenhangskomponente 38 Komplexität von BFS • BFS erste Schleife (for) wird |V | mal durchlaufen: ⇥(|V |) • zweite geschachtelte Schleife (while und for) wird im schlechtesten Fall je einmal für jeden Knoten in Adjazenzliste aufgerufen X |adj(v )| = ⇥(|E |) v 2V • Gesamtlaufzeit: ⇥(|V | + |E |) 39 Anwendungen von BFS • Besuche alle Knoten in Zusammenhangskomponente von Graph • ausgehend von Startknoten s • Berechne Länge der kürzesten Pfade (d.h. Anzahl von Kanten) • ausgehend von Startknoten s • kürzeste Pfade selbst erfordern Modifikation des Algorithmus 40 Zusammenfassung 7 Fortgeschrittene Datenstrukturen 8 Such-Algorithmen 9 Graph-Algorithmen Tiefensuche Breitensuche 41