package graph; import java.awt.*; import java.util.*; import java.util.List; import java.util.Queue; import logging.GraphLogElement; import logging.LogElement; import logging.LogElementList; import logging.NewLogElement; import testApplication.TestLogElement; import visualizationElements.EdgeStyle; import javax.swing.*; public class DirectedGraph extends Graph { private boolean hasCycle = false; LogElementList logList = new LogElementList<>(); // Erzeugen einer LogElementList für die Protokollierung LogElementList graphLogList = new LogElementList<>(); // Erzeugen einer LogElementListe von TestLogElementen, in der der Zustand des Graphen protokolliert wird private visualizationElements.Graph visualizedGraph; int step = 0; public DirectedGraph() { super(); this.visualizedGraph = new visualizationElements.Graph(new Vector(), new Vector (), true, EdgeStyle.Direct); } public DirectedGraph(String s) { super(s); this.visualizedGraph = new visualizationElements.Graph(new Vector(), new Vector (), true, EdgeStyle.Direct); } public visualizationElements.Graph getVisualizedGraph() { return this.visualizedGraph; } public visualizationElements.Graph copyVisualizedGraph() { // Neue Instanz des Graphen erstellen visualizationElements.Graph snapshotOfVisualizedGraph = new visualizationElements.Graph(new Vector<>(), new Vector<>(), true, EdgeStyle.Direct); // Kopiere alle Vertexes Vector snapshotOfVertexes = new Vector<>(); // Iteration über alle Vertexe for (visualizationElements.Vertex vertex : this.visualizedGraph.getVertexes()) { // Dem Vector der Vertexe alle Vertexe des visualizedGraph hinzufügen, wobei die Methoden für x,y,Marking und Farbe abgerufen werden snapshotOfVertexes.add(new visualizationElements.Vertex(vertex.getXpos(), vertex.getYpos(), vertex.getMarking(), vertex.getColor())); } snapshotOfVisualizedGraph.setVertexes(snapshotOfVertexes); // Setze die kopierten Vertexes im neuen Graphen // Kopiere alle Edges Vector snapshotOfEdges = new Vector<>(); for (visualizationElements.Edge edge : this.visualizedGraph.getEdges()) { // Dem Vector der Edges alle Edges des visualizedGraph hinzufügen, auch hier wieder Methodenabruf für Source,Destination, Marking und Farbe snapshotOfEdges.add(new visualizationElements.Edge(edge.getSource(), edge.getDestination(), edge.getMarking(), edge.getColor())); } snapshotOfVisualizedGraph.setEdges(snapshotOfEdges); // Setze die kopierten Edges im neuen Graphen return snapshotOfVisualizedGraph; } public void addEdge(MarkedEdge e){ super.addEdge(e); this.visualizedGraph.getEdges().add(e.getScreenEdge()); } public void addVertex(MarkedVertex v){ super.addVertex(v); this.visualizedGraph.getVertexes().add(v.getScreenVertex()); } public boolean removeEdge(MarkedEdge e){ super.removeEdge(e); this.visualizedGraph.getEdges().remove(e.getScreenEdge()); return true; } public boolean removeVertex(MarkedVertex v){ super.removeVertex(v); this.visualizedGraph.getVertexes().remove(v.getScreenVertex()); return true; } public Vector getEdges() { return this.visualizedGraph.getEdges(); } public Vector getVertexes() { return this.visualizedGraph.getVertexes(); } public LogElementList getLogList(){ return this.logList; } public LogElementList getNewLogList(){ return this.graphLogList; } public Vector> getPredecessors(MarkedVertex n) { Vector> predecessors = new Vector<>(); for (MarkedEdge edge : getAllEdges()) { if (edge.getDestination().equals(n)) { predecessors.add((MarkedVertex) edge.getSource()); } } return predecessors; } public Vector> getSuccessors(MarkedVertex n) { Vector> successors = new Vector<>(); for (MarkedEdge edge : getAllEdges()) { if (edge.getSource().equals(n)) { successors.add((MarkedVertex) edge.getDestination()); } } return successors; } public int inDegree(MarkedVertex n) { return getPredecessors(n).size(); } public int inDegree(String s) { for (MarkedVertex vertex : getAllVertexes()) { if (vertex.getName().equals(s)) { return inDegree(vertex); } } return 0; } public int outDegree(MarkedVertex n) { return getSuccessors(n).size(); } public int outDegree(String s) { for (MarkedVertex vertex : getAllVertexes()) { if (vertex.getName().equals(s)) { return outDegree(vertex); } } return 0; } public boolean areStrongAdjacent(MarkedVertex n1, MarkedVertex n2) { // Implement the logic for strong adjacency in directed graphs return (hasEdge(n1, n2) && hasEdge(n2, n1)); } public boolean areStrongAdjacent(String s1, String s2) { // Implement the logic for strong adjacency using names return (hasEdge(s1, s2)&&hasEdge(s2, s1)); } @Override public boolean areAdjacent(MarkedVertex n1, MarkedVertex n2) { return hasEdge(n1, n2); } @Override public boolean areAdjacent(String s1, String s2) { return hasEdge(s1, s2); } public Vector> breadthFirstSearch(MarkedVertex startVertex) { // Anlegen der Maps, die Farbe udn Distanz speichern Map, String> color = new HashMap<>(); Map, Integer> distance = new HashMap<>(); // Vector für die Rückgabe aller gefundenen Knoten Vector> vertices = new Vector<>(); // Schlange von MarkedVertex als LinkedList Queue> queue = new LinkedList<>(); // step für die Protokollierung step = 0; this.graphLogList.add(new TestLogElement(0, "0", 0, this.copyVisualizedGraph())); for (MarkedVertex v : getAllVertexes()) { color.put(v, "weiß"); // Alle Knoten zunächst weiß distance.put(v, Integer.MAX_VALUE); // Unendliche Distanz v.getScreenVertex().setColor(Color.white); // Setzen der Farbe weiß, des ScreenVertex // Protokollierung für Konsolenausgabe & Visualisierung logList.add(new LogElement(step++, "Farbe weiß gesetzt für: " + v.getName() + ", Distanz auf unendlich gesetzt.")); this.graphLogList.add(new TestLogElement(1, "0", 0,this.copyVisualizedGraph())); } // Startknoten initialisieren und als Grau markieren color.put(startVertex, "grau"); // in Map startVertex.getScreenVertex().setColor(Color.gray); // als ScreenVertex this.graphLogList.add(new TestLogElement(2, "0", 0,this.copyVisualizedGraph())); // startVertex in Distanzmap, in Queue und in gefundene Vertexe distance.put(startVertex, 0); queue.add(startVertex); vertices.add(startVertex); logList.add(new LogElement(step++, "Startknoten " + startVertex.getName() + " auf grau gesetzt, Distanz = 0.")); while (!queue.isEmpty()) { MarkedVertex current = queue.poll(); // Oberstes Element der Schlange als momentanten Knoten setzen logList.add(new LogElement(step++, "Dequeue Knoten: " + current.getName())); // Protokollierung // Prozessieren aller Nachbarn des aktuellen Knotens for (MarkedVertex neighbor : getNeighbors(current)) { if (color.get(neighbor).equals("weiß")) { // Wenn der Nachbar noch nicht besucht wurde color.put(neighbor, "grau"); neighbor.getScreenVertex().setColor(Color.gray); // alle Nachbarn des aktuellen Knoten werden in die Maps und in die Queue aufgenommen distance.put(neighbor, distance.get(current) + 1); queue.add(neighbor); vertices.add(neighbor); // Protokollierung logList.add(new LogElement(step++, "Nachbar " + neighbor.getName() + " auf grau gesetzt, Distanz erhöht zu " + distance.get(neighbor))); this.graphLogList.add(new TestLogElement(3, "0", 0,this.copyVisualizedGraph())); } } current.getScreenVertex().setColor(Color.black); color.put(current, "schwarz"); // Markieren des aktuellen Knotens als vollständig verarbeitet queue.remove(current); logList.add(new LogElement(step++, "Knoten " + current.getName() + " auf schwarz gesetzt.")); this.graphLogList.add(new TestLogElement(4, "0", 0,this.copyVisualizedGraph())); } logResults(); return vertices; } private List> getNeighbors(MarkedVertex vertex) { List> neighbors = new ArrayList<>(); for (MarkedEdge edge : getAllEdges()) { if (edge.getSource().equals(vertex)) { neighbors.add((MarkedVertex) edge.getDestination()); } // Breitensuche in beide Richtungen, da auch Nachbarn, die auf den Knoten zeigen, als Nachbarn erkannt werden (kann verändert werden, indem man einfach das else if ausklammert) else if (edge.getDestination().equals(vertex)){ neighbors.add((MarkedVertex) edge.getSource()); } } return neighbors; } Map, Integer> ToSoNr = new HashMap<>(); Map, String> Besucht = new HashMap<>(); int nummer = getAllVertexes().size(); public boolean topSort(MarkedVertex n1){ // Zurücksetzen der verwendeten Maps Besucht.clear(); ToSoNr.clear(); step = 0; // Step als 0 deklarieren als Protokollierungsvariable n1.getScreenVertex().setColor(Color.white); // Startknoten n1 als weiß setzen, für bessere Übersichtlichkeit bei Visualisierung this.graphLogList.add(new TestLogElement(0, "0", 0,this.copyVisualizedGraph())); // Übergabe aktueller Graph an graphLogList for (MarkedVertex vertex : getAllVertexes()){ // Für jeden Vertex überprüfe, ob er schon in Besucht vorhanden ist if (!Besucht.containsKey(vertex)){ // Bei Reihenfolge von Eingabe A,B,C erst den A nehmen, und dann Rekursion bis C logList.add(new LogElement(step++, "Nicht besucht: " + vertex.getName())); tsprozedur(vertex); // Übergabe von unbesuchtem Knoten an tsprozedur } } // Wenn die Knoten alle abgearbeitet wurden ohne Zyklus: if (!hasCycle) { logList.add(new LogElement(step++, "Topologische Sortierung erfolgreich.")); } // Ausgabe der ToSoNr Map auf der Konsole for (Map.Entry, Integer> entry : ToSoNr.entrySet()) { MarkedVertex vertex = entry.getKey(); Integer num = entry.getValue(); System.out.println("Vertex: " + vertex.getName() + ", Nummer: " + num); } logResults(); return hasCycle; } public void tsprozedur(MarkedVertex n1){ // Knoten als in Bearbeitung markieren Besucht.put(n1, "in Bearbeitung"); logList.add(new LogElement(step++, "Wurde besucht - " + n1.getName())); // Logging for (MarkedVertex vertex : getSuccessors(n1)) { // Zyklus gefunden, wenn der Nachfolger bereits in Bearbeitung ist // Wenn Nachfolger in Besucht existiert und "in Bearbeitung" markiert ist und es nicht der direkte Vorgänger ist (Doppelkante) if (Besucht.get(vertex) != null && Besucht.get(vertex).equals("in Bearbeitung") && !getPredecessors(n1).contains(vertex)) { logList.add(new LogElement(step++, "Zyklus gefunden bei Knoten " + vertex.getName() + " von " + n1.getName())); vertex.getScreenVertex().setColor(Color.red); n1.getScreenVertex().setColor(Color.red); this.graphLogList.add(new TestLogElement(2, "0", 0,this.copyVisualizedGraph())); // In graphLogList wird eine aktuelle Instanz des Graphen gespeichert hasCycle = true; return; } // Falls Nachfolger noch nicht besucht wurde -> Eine Rekursion tiefer mit Nachfolger else if (!Besucht.containsKey(vertex)) { tsprozedur(vertex); if (hasCycle) return; // Stoppe weitere Verarbeitung, wenn Zyklus gefunden } } // Markiere Knoten als vollständig besucht // Wenn es keinen Nachfolger mehr zum rekursiven Aufruf gibt Besucht.put(n1, "besucht"); // Setze Knoten n1 in Besucht-Map auf "besucht" ToSoNr.put(n1, (nummer--)+getAllVertexes().size()); //Setze Knoten n1 in TopSortNummer-Map auf Anzahl aller Knoten - nummer(bei letztem Knoten -1) logList.add(new LogElement(step++, "Knoten " + n1.getName() + " als vollständig besucht markiert. Topologische Nummer:" + ToSoNr.get(n1))); // Protokollierung für Konsolenausgabe n1.getScreenVertex().setMarking(String.valueOf(ToSoNr.get(n1))); // Setze die Bezeichnung der Knoten als Nummer für die Visualisierung this.graphLogList.add(new TestLogElement(1, "0", 0,this.copyVisualizedGraph())); // In graphLogList wird eine aktuelle Instanz des Graphen gespeichert } public boolean hasCycle(MarkedVertex n1){ return topSort(n1); } private void logResults() { // Iteration über logList mit Konsolenausgabe for (LogElement element : logList) { System.out.println("Step " + element.getStep() + ": " + element.getDescription()); } } }