8 Java: Datenstrukturen für Graphen 8.1 Datenstrukturen für Graphen 8.2 Graphen als dynamische Datenstruktur 8 8.3 Graphen als Kantenliste 8.4 Graphen als Knotenliste 8.5 Graphen als Adjazenzmatrix 8.6 Graphen als Adjazenzlisten 109 8 Java: Datenstrukturen für Graphen Teil VIII Java: Datenstrukturen für Graphen Überblick Datenstrukturen für Graphen Graphen als dynamische Datenstruktur Graphen als Kantenliste Graphen als Knotenliste Graphen als Adjazenzmatrix Graphen als Adjazenzlisten Saake/Schallehn Algorithmen & Datenstrukturen II 8–1 Datenstrukturen für Graphen ◮ Verschiedene mögliche Implementierungsalternativen ◮ Auswahl je nach Anwendung und Aufwand für Operationen ◮ ◮ Hier: Grundprinzipien für verschiedene Alternativen vorgestellt Eingeschränkte Implementierungen ◮ ◮ ◮ Gerichteter, ungewichteter Graph Durchgehend durchnummerierte Knoten Nur Erzeugen von Knoten und Kanten, kein Löschen Saake/Schallehn 110 Algorithmen & Datenstrukturen II 8–2 Uni Magdeburg, WS 2005/06 8 Java: Datenstrukturen für Graphen Allgemeine Schnittstelle für Beispielimplementierung ◮ ◮ Schnittstelle in Graph.java Implementiert als dynamische Datenstruktur, Knotenliste, Kantenliste, Adjazenzliste und Adjazenzmatrix public interface Graph { 8 public int addNode(); public boolean addEdge(int orig, int dest); } Saake/Schallehn Algorithmen & Datenstrukturen II 8–3 Testrahmen ◮ Testrahmen in GraphTest.java ... Graph elg = new EdgeListGraph(); for (int i = 1; i <= 4; i++) elg.addNode(); for (int i = 0; i < 10; i++) { int orig = 1 + (int) (4 * Math.random()); int dest = 1 + (int) (4 * Math.random()); elg.addEdge(orig,dest); } ... Saake/Schallehn Algorithmen & Datenstrukturen II 8–4 Graphen als dynamische Datenstruktur ◮ ◮ Vergleichsweise aufwändige Implementierung und komplexe Operationen Grundidee entsprechend Bäumen ◮ ◮ ◮ ◮ ◮ ◮ Graph besteht aus Objekten einer speziellen Knotenklasse Kanten umgesetzt durch Java-Referenzen EntryPoints als Knoten ohne eingehende Kanten (entspricht etwa Baumwurzel) EntryPoints erlauben Zugriff auf alle Graphenknoten durch direkte oder indirekte Erreichbarkeit Gewichtete Graphen würden Kantenklasse erfordern Ungerichtete Graphen würden bidirektionale Referenzen erfordern (entsprechend doppelter Verkettung) Saake/Schallehn Algorithmen & Datenstrukturen II Uni Magdeburg, WS 2005/06 8–5 111 8 Java: Datenstrukturen für Graphen Dynamisch: Klassenstruktur public class DynamicGraph implements Graph { public class GraphNode implements Comparable<GraphNode> { int key; Set<GraphNode> edges = null; GraphNode(int k) { key = k; edges = new TreeSet<GraphNode>(); } ... } } Saake/Schallehn Algorithmen & Datenstrukturen II 8–6 Dynamisch: Klassenstruktur public class DynamicGraph implements Graph { ... Set<GraphNode> entryPoints = null; int maxKey = 0; DynamicGraph() { entryPoints = new TreeSet<GraphNode>(); } ... } Saake/Schallehn Algorithmen & Datenstrukturen II 8–7 Dynamisch: Knoten hinzufügen ◮ ◮ ◮ Einfaches Erzeugen eines neuen Knotens als EntryPoint Gewährleistung der Erreichbarkeit, da bisher keine Kante zu diesem Knoten möglich Durchnumerierung der Knoten public int addNode() { entryPoints.add(new GraphNode(++maxKey)); return maxKey; } Saake/Schallehn 112 Algorithmen & Datenstrukturen II 8–8 Uni Magdeburg, WS 2005/06 8 Java: Datenstrukturen für Graphen Dynamisch: Knoten Suchen ◮ Kanten Hinzufügen erfordert Finden von Knoten Einfacher Suchalgorithmus ◮ ◮ ◮ ◮ ◮ Kompletter Durchlauf des Graphen Todo: Merken der noch zu besuchenden Knoten in Menge, entspricht anfangs EntryPoints Done: Merken der schon besuchten Knoten in weiterer Menge zur Vermeidung von Zyklen Beim Besuchen jeden Knotens hinzufügen der Knoten ausgehender Kanten zu Todo-Menge, wenn diese noch nicht in Done-Menge sind Abbruch wenn Knoten gefunden oder alle Knoten durchsucht Saake/Schallehn Algorithmen & Datenstrukturen II 8 ◮ 8–9 Dynamisch: Knoten Suchen /2 GraphNode findNode(int k) { TreeSet<GraphNode> todoSearch = new TreeSet<GraphNode>(); todoSearch.addAll(entryPoints); Set<GraphNode> searchDone = new TreeSet<GraphNode>(); while(!todoSearch.isEmpty()) { GraphNode n = todoSearch.first(); if (n.key == k) return n; todoSearch.remove(n); searchDone.add(n); for (GraphNode e : n.edges) if (!searchDone.contains(e)) todoSearch.add(e); } return null; } Saake/Schallehn Algorithmen & Datenstrukturen II 8–10 Dynamisch: Kante Hinzufügen 1. Finden von Ausgangs- und Zielknoten 2. Setzen der Kantenbeziehung 3. Da Zielknoten nun indirekt erreichbar ist, kann er aus den EntryPoints entfernt werden 4. Mögliches Problem: durch einen Zyklus ist nun der Ausgangsknoten nicht mehr erreichbar ◮ ◮ Einaches Beispiel: Einfügen der Kante 1 → 1 Ist der Ausgangsknoten dadurch nicht mehr erreichbar, wird er wieder zu den EntryPoints hinzugefügt Saake/Schallehn Algorithmen & Datenstrukturen II Uni Magdeburg, WS 2005/06 8–11 113 8 Java: Datenstrukturen für Graphen Dynamisch: Kante Hinzufügen /2 public boolean addEdge(int orig, int dest) { GraphNode origNode = findNode(orig); GraphNode destNode = findNode(dest); if (origNode == null || destNode == null) return false; origNode.edges.add(destNode); entryPoints.remove(destNode); if (findNode(orig) == null) entryPoints.add(origNode); return true; } Saake/Schallehn Algorithmen & Datenstrukturen II 8–12 Graphen als Kantenliste ◮ ◮ ◮ Erstes Element: Anzahl der Knoten Zweites Element: Anzahl der Kanten Alle weiteren Wertepaare: Kanten-Codierungen 1 2 3 4 5 6 6, 11, 1, 2, 1, 3, 3, 1, 4, 1, 3, 4, 3, 6, 5, 3, 5, 5, 6, 5, 6, 2, 6, 4 Saake/Schallehn Algorithmen & Datenstrukturen II 8–13 Graphen als Kantenliste /2 ◮ Array-basiert Implementierung Knoten hinzufügen: ◮ Kante hinzufügen: ◮ ◮ ◮ ◮ ◮ ◮ Hochsetzen des Knotenzählers Zwei Einträge an Ende des Arrays anhängen Hochsetzen des Kantenzählers Problemfall 1: Knoten existieren nicht Problemfall 2: Kante existiert bereits Saake/Schallehn 114 Algorithmen & Datenstrukturen II 8–14 Uni Magdeburg, WS 2005/06 8 Java: Datenstrukturen für Graphen Kantenliste: Knoten Hinzufügen public class EdgeListGraph implements Graph { private int[] edgeList = {0,0}; public int addNode() { return ++edgeList[0]; } ... 8 } Saake/Schallehn Algorithmen & Datenstrukturen II 8–15 Kantenliste: Kante Hinzufügen /1 public boolean addEdge(int orig, int dest) { if (orig > edgeList[0] || dest > edgeList[0] || orig < 1 || dest < 1) return false; for (int i = 2; i < edgeList.length; i += 2) if (edgeList[i] == orig && edgeList[i+1] == dest) return false; ... Saake/Schallehn Algorithmen & Datenstrukturen II 8–16 Kantenliste: Kante Hinzufügen /2 ... int[] newEdgeList = new int[edgeList.length+2]; System.arraycopy(edgeList,0, newEdgeList,0,edgeList.length); newEdgeList[newEdgeList.length-2]=orig; newEdgeList[newEdgeList.length-1]=dest; edgeList = newEdgeList; newEdgeList[1]++; return true; } Saake/Schallehn Algorithmen & Datenstrukturen II Uni Magdeburg, WS 2005/06 8–17 115 8 Java: Datenstrukturen für Graphen Graphen als Knotenliste ◮ ◮ ◮ Erstes Element: Anzahl der Knoten Zweites Element: Anzahl der Kanten Danach für jeden Knoten: Codierung der Anzahl ausgehender Kanten n und der n Zielknoten 1 2 3 4 5 6 6, 11, 2, 2, 3, 0, 3, 1, 4, 6, 1, 1, 2, 3, 5, 3, 2, 4, 5 Saake/Schallehn Algorithmen & Datenstrukturen II 8–18 Graphen als Knotenliste /2 ◮ ◮ Array-basiert Implementierung Knoten hinzufügen: ◮ ◮ ◮ Hochsetzen des Knotenzählers Anhängen einer 0 als Knotengrad des neuen Knotens an Array Kante hinzufügen: ◮ ◮ ◮ ◮ ◮ ◮ Position des Knotens im Array finden Zielknoten in Array einfügen Hochsetzen des Knotengrads Hochsetzen des Kantenzählers Problemfall 1: Knoten existieren nicht Problemfall 2: Kante existiert bereits Saake/Schallehn Algorithmen & Datenstrukturen II 8–19 Knotenliste: Knoten Einfügen public class NodeListGraph implements Graph { private int[] nodeList = {0,0}; public int addNode() { int[] newNodeList = new int[nodeList.length+1]; System.arraycopy(nodeList,0, newNodeList,0,nodeList.length); newNodeList[newNodeList.length-1]=0; nodeList = newNodeList; nodeList[0]++; return nodeList[0]; } ... } Saake/Schallehn 116 Algorithmen & Datenstrukturen II 8–20 Uni Magdeburg, WS 2005/06 8 Java: Datenstrukturen für Graphen Knotenliste: Kante Einfügen /1 Saake/Schallehn Algorithmen & Datenstrukturen II 8 public boolean addEdge(int orig, int dest) { if (orig > nodeList[0] || dest > nodeList[0] || orig < 1 || dest < 1) return false; int pos = 2; for (int i = 1; i < orig; i++) pos += 1 + nodeList[pos]; for (int i = 1; i <= nodeList[pos]; i++) if (nodeList[pos+i] == dest) return false; ... 8–21 Knotenliste: Kante Einfügen /2 ... int[] newNodeList = new int[nodeList.length+1]; System.arraycopy(nodeList,0,newNodeList,0,pos+1); System.arraycopy(nodeList,pos+1, newNodeList,pos+2,nodeList.length-pos-1); newNodeList[pos]++; newNodeList[pos+1]=dest; newNodeList[1]++; nodeList = newNodeList; return true; } Saake/Schallehn Algorithmen & Datenstrukturen II 8–22 Graphen als Adjazenzmatrix Gg = 0 0 1 1 0 0 1 0 0 0 0 1 1 0 0 0 1 0 0 0 1 0 0 1 0 0 0 0 1 1 0 0 1 0 0 0 1 2 Saake/Schallehn 3 4 5 6 Algorithmen & Datenstrukturen II Uni Magdeburg, WS 2005/06 8–23 117 8 Java: Datenstrukturen für Graphen Graphen als Adjazenzmatrix /2 ◮ ◮ Zweidimensionales Array von boolean Knoten hinzufügen: ◮ ◮ Vergrößern der Adjazenzmatrix um jeweils eine zusätzliche leere Spalte und Zeile Kante hinzufügen: ◮ Setzen eines Bits in der Adjazenzmatrix Saake/Schallehn Algorithmen & Datenstrukturen II 8–24 Adjazenzmatrix: Datenstruktur public class AdjacencyMatrixGraph implements Graph { private boolean[][] adjacencyMatrix = null; ... } Saake/Schallehn Algorithmen & Datenstrukturen II 8–25 Adjazenzmatrix: Knoten hinzufügen public int addNode() { int nodeNumber = (adjacencyMatrix == null) ? 0 : adjacencyMatrix.length; boolean[][] newAdjacencyMatrix = new boolean[nodeNumber+1][nodeNumber+1]; for (int i = 0; i < nodeNumber+1; i++) for (int j = 0; j < nodeNumber+1; j++) if (i == nodeNumber || j == nodeNumber) newAdjacencyMatrix[i][j]=false; else newAdjacencyMatrix[i][j]= adjacencyMatrix[i][j]; adjacencyMatrix = newAdjacencyMatrix; return nodeNumber+1; } Saake/Schallehn 118 Algorithmen & Datenstrukturen II 8–26 Uni Magdeburg, WS 2005/06 8 Java: Datenstrukturen für Graphen Adjazenzmatrix: Kante Hinzufügen Saake/Schallehn Algorithmen & Datenstrukturen II 8 public boolean addEdge(int orig, int dest) { int nodeNumber = (adjacencyMatrix == null) ? 0 : adjacencyMatrix.length; if (orig > nodeNumber || dest > nodeNumber || orig < 1 || dest < 1) return false; if (adjacencyMatrix[orig-1][dest-1]) return false; adjacencyMatrix[orig-1][dest-1] = true; return true; } 8–27 Graphen als Adjazenzlisten 1 2 3 3 1 4 4 1 5 3 5 6 2 4 2 Saake/Schallehn 6 5 Algorithmen & Datenstrukturen II 8–28 Graphen als Adjazenzlisten /2 ◮ Dynamik umsetzbar als Liste von Listen ◮ Hier Array (Knoten) von Arrays (adjazente Knoten) → ebenfalls zweidimensionales Array Knoten hinzufügen: ◮ Kante hinzufügen: ◮ ◮ ◮ Knoten-Array wächst um eine Position Array für adjazente Knoten wächst um einen Eintrag Saake/Schallehn Algorithmen & Datenstrukturen II Uni Magdeburg, WS 2005/06 8–29 119 8 Java: Datenstrukturen für Graphen Adjazenzliste: Datenstruktur public class AdjacencyListGraph implements Graph { private int[][] adjacencyList = null; ... } Saake/Schallehn Algorithmen & Datenstrukturen II 8–30 Adjazenzliste: Knoten Hinzufügen public int addNode() { int nodeNumber = (adjacencyList == null) ? 0 : adjacencyList.length; int[][] newAdjacencyList = new int[nodeNumber+1][]; for (int i = 0; i < nodeNumber; i++) newAdjacencyList[i]=adjacencyList[i]; newAdjacencyList[nodeNumber] = null; adjacencyList = newAdjacencyList; return nodeNumber+1; } Saake/Schallehn Algorithmen & Datenstrukturen II 8–31 Adjazenzliste: Kante Hinzufügen /1 public boolean addEdge(int orig, int dest) { int nodeNumber = (adjacencyList == null) ? 0 : adjacencyList.length; if (orig > nodeNumber || dest > nodeNumber || orig < 1 || dest < 1) return false; if (adjacencyList[orig-1] != null) for (int n : adjacencyList[orig-1]) if (n == dest) return false; if (adjacencyList[orig-1] == null) { adjacencyList[orig-1] = new int[1]; adjacencyList[orig-1][0] = dest; } ... Saake/Schallehn 120 Algorithmen & Datenstrukturen II 8–32 Uni Magdeburg, WS 2005/06 8 Java: Datenstrukturen für Graphen Adjazenzliste: Kante Hinzufügen /1 8 ... else { int[] newList = new int[adjacencyList[orig-1].length+1]; System.arraycopy(adjacencyList[orig-1],0, newList,0,adjacencyList[orig-1].length); newList[adjacencyList[orig-1].length] = dest; adjacencyList[orig-1] = newList; } return true; } Saake/Schallehn Algorithmen & Datenstrukturen II Uni Magdeburg, WS 2005/06 8–33 121