Verificando se um Gráfico Java possui um Ciclo
1. Visão geral
Neste tutorial rápido, aprenderemos como podemosdetect a cycle in a given directed graph.
2. Representação gráfica
Para este tutorial, ficaremos com a lista de adjacênciasgraph representation.
Em primeiro lugar, vamos começar definindo umVertex em Java:
public class Vertex {
private String label;
private boolean beingVisited;
private boolean visited;
private List adjacencyList;
public Vertex(String label) {
this.label = label;
this.adjacencyList = new ArrayList<>();
}
public void addNeighbor(Vertex adjacent) {
this.adjacencyList.add(adjacent);
}
//getters and setters
}
Here, the adjacencyList of a vertex v holds a list of all vertices adjacent to v. O métodoaddNeighbor() adiciona um vértice vizinho à lista de adjacências dev.
Também definimos dois parâmetrosboolean,beingVisited and visited, which represent whether the node is currently being visited or has already been visited.
Um gráfico pode ser pensado como um grupo de vértices ou nós conectados através das arestas.
Então, vamos agora representar rapidamente umGraph em Java:
public class Graph {
private List vertices;
public Graph() {
this.vertices = new ArrayList<>();
}
public void addVertex(Vertex vertex) {
this.vertices.add(vertex);
}
public void addEdge(Vertex from, Vertex to) {
from.addNeighbor(to);
}
// ...
}
Usaremos os métodosaddVertex()eaddEdge() para adicionar novos vértices e arestas em nosso gráfico.
3. Detecção de ciclo
Para detectar um ciclo em um gráfico direcionado,we’ll use a variation of DFS traversal:
-
Pegue um vértice não visitadov e marque seu estado comobeingVisited
-
Para cada vértice vizinhou dev, verifique:
-
Seu já estiver no estadobeingVisited, isso significa claramentethere exists a backward edge and so a cycle has been detected
-
Seu ainda estiver em um estado não visitado, visitaremos recursivamenteu em profundidade
-
-
Atualize o sinalizadorv'sbeingVisited do vértice parafalse e o sinalizadorvisited paratrue
Observe queall the vertices of our graph are initially in an unvisited state as both their beingVisited and visited flags are initialized with false.
Vamos agora dar uma olhada em nossa solução Java:
public boolean hasCycle(Vertex sourceVertex) {
sourceVertex.setBeingVisited(true);
for (Vertex neighbor : sourceVertex.getAdjacencyList()) {
if (neighbor.isBeingVisited()) {
// backward edge exists
return true;
} else if (!neighbor.isVisited() && hasCycle(neighbor)) {
return true;
}
}
sourceVertex.setBeingVisited(false);
sourceVertex.setVisited(true);
return false;
}
Podemos usar qualquer vértice em um gráfico para ser a fonte ou o vértice inicial.
Para um gráfico desconectado, teremos que adicionar um método de wrapper adicional:
public boolean hasCycle() {
for (Vertex vertex : vertices) {
if (!vertex.isVisited() && hasCycle(vertex)) {
return true;
}
}
return false;
}
Isso é para garantir que visitamos todos os componentes de um gráfico desconectado para detectar um ciclo.
4. Teste de Implementação
Vamos considerar o gráfico direcionado cíclico abaixo:
Podemos escrever rapidamente um JUnit para verificar nosso métodohasCycle() para este gráfico:
@Test
public void givenGraph_whenCycleExists_thenReturnTrue() {
Vertex vertexA = new Vertex("A");
Vertex vertexB = new Vertex("B");
Vertex vertexC = new Vertex("C")
Vertex vertexD = new Vertex("D");
Graph graph = new Graph();
graph.addVertex(vertexA);
graph.addVertex(vertexB);
graph.addVertex(vertexC);
graph.addVertex(vertexD);
graph.addEdge(vertexA, vertexB);
graph.addEdge(vertexB, vertexC);
graph.addEdge(vertexC, vertexA);
graph.addEdge(vertexD, vertexC);
assertTrue(graph.hasCycle());
}
Aqui, nosso métodohasCycle() retornoutrue denotando que nosso gráfico é cíclico.
5. Conclusão
Neste tutorial, aprendemos como verificar se existe um ciclo em um determinado gráfico direcionado em Java.
Como de costume, a implementação do código com exemplos está disponívelover on Github.