331 lines
15 KiB
Java
331 lines
15 KiB
Java
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<T extends VertexMarking, U extends EdgeMarking> extends Graph<T, U> {
|
|
|
|
private boolean hasCycle = false;
|
|
|
|
LogElementList<LogElement> logList = new LogElementList<>(); // Erzeugen einer LogElementList für die Protokollierung
|
|
LogElementList<TestLogElement> 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<visualizationElements.Vertex>(), new Vector <visualizationElements.Edge>(), true, EdgeStyle.Direct);
|
|
}
|
|
|
|
public DirectedGraph(String s) {
|
|
super(s);
|
|
this.visualizedGraph = new visualizationElements.Graph(new Vector<visualizationElements.Vertex>(), new Vector <visualizationElements.Edge>(), 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<visualizationElements.Vertex> 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<visualizationElements.Edge> 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<U> e){
|
|
super.addEdge(e);
|
|
this.visualizedGraph.getEdges().add(e.getScreenEdge());
|
|
}
|
|
public void addVertex(MarkedVertex<T> v){
|
|
super.addVertex(v);
|
|
this.visualizedGraph.getVertexes().add(v.getScreenVertex());
|
|
}
|
|
public boolean removeEdge(MarkedEdge<U> e){
|
|
super.removeEdge(e);
|
|
this.visualizedGraph.getEdges().remove(e.getScreenEdge());
|
|
return true;
|
|
}
|
|
public boolean removeVertex(MarkedVertex<T> v){
|
|
super.removeVertex(v);
|
|
this.visualizedGraph.getVertexes().remove(v.getScreenVertex());
|
|
return true;
|
|
}
|
|
public Vector<visualizationElements.Edge> getEdges() {
|
|
return this.visualizedGraph.getEdges();
|
|
}
|
|
|
|
public Vector<visualizationElements.Vertex> getVertexes() {
|
|
return this.visualizedGraph.getVertexes();
|
|
}
|
|
|
|
public LogElementList<LogElement> getLogList(){
|
|
return this.logList;
|
|
}
|
|
|
|
public LogElementList<TestLogElement> getNewLogList(){
|
|
return this.graphLogList;
|
|
}
|
|
|
|
public Vector<MarkedVertex<T>> getPredecessors(MarkedVertex<T> n) {
|
|
Vector<MarkedVertex<T>> predecessors = new Vector<>();
|
|
for (MarkedEdge<U> edge : getAllEdges()) {
|
|
if (edge.getDestination().equals(n)) {
|
|
predecessors.add((MarkedVertex<T>) edge.getSource());
|
|
}
|
|
}
|
|
return predecessors;
|
|
}
|
|
|
|
public Vector<MarkedVertex<T>> getSuccessors(MarkedVertex<T> n) {
|
|
Vector<MarkedVertex<T>> successors = new Vector<>();
|
|
for (MarkedEdge<U> edge : getAllEdges()) {
|
|
if (edge.getSource().equals(n)) {
|
|
successors.add((MarkedVertex<T>) edge.getDestination());
|
|
}
|
|
}
|
|
return successors;
|
|
}
|
|
|
|
public int inDegree(MarkedVertex<T> n) {
|
|
return getPredecessors(n).size();
|
|
}
|
|
|
|
public int inDegree(String s) {
|
|
for (MarkedVertex<T> vertex : getAllVertexes()) {
|
|
if (vertex.getName().equals(s)) {
|
|
return inDegree(vertex);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
public int outDegree(MarkedVertex<T> n) {
|
|
return getSuccessors(n).size();
|
|
}
|
|
|
|
public int outDegree(String s) {
|
|
for (MarkedVertex<T> vertex : getAllVertexes()) {
|
|
if (vertex.getName().equals(s)) {
|
|
return outDegree(vertex);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
public boolean areStrongAdjacent(MarkedVertex<T> n1, MarkedVertex<T> 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<T> n1, MarkedVertex<T> n2) {
|
|
return hasEdge(n1, n2);
|
|
}
|
|
|
|
@Override
|
|
public boolean areAdjacent(String s1, String s2) {
|
|
return hasEdge(s1, s2);
|
|
}
|
|
|
|
|
|
public Vector<MarkedVertex<T>> breadthFirstSearch(MarkedVertex<T> startVertex) {
|
|
// Anlegen der Maps, die Farbe udn Distanz speichern
|
|
Map<MarkedVertex<T>, String> color = new HashMap<>();
|
|
Map<MarkedVertex<T>, Integer> distance = new HashMap<>();
|
|
// Vector für die Rückgabe aller gefundenen Knoten
|
|
Vector<MarkedVertex<T>> vertices = new Vector<>();
|
|
// Schlange von MarkedVertex als LinkedList
|
|
Queue<MarkedVertex<T>> queue = new LinkedList<>();
|
|
// step für die Protokollierung
|
|
step = 0;
|
|
|
|
this.graphLogList.add(new TestLogElement(0, "0", 0, this.copyVisualizedGraph()));
|
|
|
|
for (MarkedVertex<T> 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<T> 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<T> 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<MarkedVertex<T>> getNeighbors(MarkedVertex<T> vertex) {
|
|
List<MarkedVertex<T>> neighbors = new ArrayList<>();
|
|
for (MarkedEdge<U> edge : getAllEdges()) {
|
|
if (edge.getSource().equals(vertex)) {
|
|
neighbors.add((MarkedVertex<T>) 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<T>) edge.getSource());
|
|
}
|
|
}
|
|
return neighbors;
|
|
}
|
|
|
|
|
|
Map<MarkedVertex<T>, Integer> ToSoNr = new HashMap<>();
|
|
Map<MarkedVertex<T>, String> Besucht = new HashMap<>();
|
|
int nummer = getAllVertexes().size();
|
|
|
|
public boolean topSort(MarkedVertex<T> 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<T> 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<MarkedVertex<T>, Integer> entry : ToSoNr.entrySet()) {
|
|
MarkedVertex<T> vertex = entry.getKey();
|
|
Integer num = entry.getValue();
|
|
System.out.println("Vertex: " + vertex.getName() + ", Nummer: " + num);
|
|
}
|
|
logResults();
|
|
return hasCycle;
|
|
}
|
|
|
|
|
|
public void tsprozedur(MarkedVertex<T> n1){
|
|
// Knoten als in Bearbeitung markieren
|
|
Besucht.put(n1, "in Bearbeitung");
|
|
logList.add(new LogElement(step++, "Wurde besucht - " + n1.getName())); // Logging
|
|
|
|
for (MarkedVertex<T> 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<T> n1){
|
|
return topSort(n1);
|
|
}
|
|
|
|
private void logResults() { // Iteration über logList mit Konsolenausgabe
|
|
for (LogElement element : logList) {
|
|
System.out.println("Step " + element.getStep() + ": " + element.getDescription());
|
|
}
|
|
}
|
|
|
|
|
|
}
|