简体   繁体   中英

How to print each cycle in a directed graph Java?

I'm in a super trouble. I really don't know how to modify the code to print each cycle that has been found. Actually the code below is returning if the graph contains a cycle, but I also want to know what are all the possible cycles.

For example, the following graph contains three cycles 0->2->0, 0->1->2->0 and 3->3, so your function must return true.

// A Java Program to detect cycle in a graph
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

class Graph {
    private final int V;
    private final List<List<Integer>> adj;

    public Graph(int V) 
    {
        this.V = V;
        adj = new ArrayList<>(V);

        for (int i = 0; i < V; i++)
            adj.add(new LinkedList<>());
    }

    // This function is a variation of DFSUytil() in 
    // https://www.geeksforgeeks.org/archives/18212
    private boolean isCyclicUtil(int i, boolean[] visited, boolean[] recStack) 
    {
        // Mark the current node as visited and
        // part of recursion stack
        if (recStack[i])
            return true;

        if (visited[i])
            return false;

        visited[i] = true;

        recStack[i] = true;
        List<Integer> children = adj.get(i);

        for (Integer c: children)
            if (isCyclicUtil(c, visited, recStack))
                return true;

        recStack[i] = false;

        return false;
    }

    private void addEdge(int source, int dest) {
        adj.get(source).add(dest);
    }

    // Returns true if the graph contains a 
    // cycle, else false.
    // This function is a variation of DFS() in 
    // https://www.geeksforgeeks.org/archives/18212
    private boolean isCyclic() 
    {
        // Mark all the vertices as not visited and
        // not part of recursion stack
        boolean[] visited = new boolean[V];
        boolean[] recStack = new boolean[V];

        // Call the recursive helper function to
        // detect cycle in different DFS trees
        for (int i = 0; i < V; i++)
            if (isCyclicUtil(i, visited, recStack))
                return true;

        return false;
    }

    // Driver code
    public static void main(String[] args)
    {
        Graph graph = new Graph(4);
        graph.addEdge(0, 1);
        graph.addEdge(0, 2);
        graph.addEdge(1, 2);
        graph.addEdge(2, 0);
        graph.addEdge(2, 3);
        graph.addEdge(3, 3);

        if(graph.isCyclic())
            System.out.println("Graph contains cycle");
        else
            System.out.println("Graph doesn't "
                                + "contain cycle");
    }
}

Thank you so much.

Edit: Previously I mentioned the possibility to use dfs instead of bfs , however using dfs might produce non-minimal cycles. (eg if a cycle A->B->C->A exists and a cylce A->B->A exists, it might find the longer one first and it won't find the second one as nodes are only visited once).

As per definition an elementary cycle is one where a node does not repeat itself (besides the starting one), so the case is a bit different. As the questioner (of the bounty @ExceptionHandler ) wanted those cycles excluded from the output, using bfs solves that issue.

For a pure (brute-force) elementary cycle search a different path finding algorithm would be required.


A general purpose (aka brute force) implementation would entail the following steps:

  1. For every node n of a directed graph g
    find all pathes (using bfs ) back to n.

    If muliple edges between two nodes (with the same direction) exist they can be ignored at this step, as the algorithm itself should work on nodes rather than edges. Multiple edges can be reintroduced into the cycles during step 5.

  2. if no pathes are found, continue in Step 1 with n+1

  3. Every identified path is a cylce
    add them to a list of cycles, and continue with Step 1 and n+1

  4. After all nodes have been processed a list containing all possible cycles have been found (including permutations). Subcycles could not have been formed as every node can only be visited once during bfs .

    In this step all permutations of previously identified are grouped in sets. Only one cylce per set is considered. This can be done by ordering the node and removing duplicates.

  5. Now the minimum set of cycles has been identified and can be printed out.
    In case you are looking for edge-specific cycles, replace the connection between two nodes with their respective edge(s).


Example for the graph A->B B->C C->D D->C C->A :

Step 1-3: node A
path identified: A,B,C (A->B B->C C->A)
Step 1-3: node B
path identified: B,C,A (B->C C->A A->B)
Step 1-3: node C
path identified: C,A,B (C->A A->B B->C)
path identified: C,D (C->D D->C)
Step 1-3: node D
path identified: D,C (D->C C->D)
Step 4:
Identified as identical after ordering:

Set1:
A,B,C (A->B B->C C->A)
B,C,A (B->C C->A A->B)
C,A,B (C->A A->B B->C)

Set2:
C,D (C->D D->C)
D,C (D->C C->D)

Therefore remaining cycles:
A,B,C (A->B B->C C->A)
C,D (C->D D->C)
Step 5:
Simply printing out the cycles
(Check the bracket expressions for that,
 I simply added them to highlight the relevant edges).

A more efficient sample implementation to identify elementary cycles can be found here , which was directly taken from this answer . If someone wants to come up with a more detailed explanation how that algorithm works exactly feel free to do so.

Modifing the main method to:

public static void main(String[] args) {
    String nodes[] = new String[4];
    boolean adjMatrix[][] = new boolean[4][4];

    for (int i = 0; i < 4; i++) {
        nodes[i] = String.valueOf((char) ('A' + i));
    }

    adjMatrix[0][1] = true;
    adjMatrix[1][2] = true;
    adjMatrix[2][3] = true;
    adjMatrix[3][2] = true;
    adjMatrix[2][0] = true;

    ElementaryCyclesSearch ecs = new ElementaryCyclesSearch(adjMatrix, nodes);
    List cycles = ecs.getElementaryCycles();
    for (int i = 0; i < cycles.size(); i++) {
        List cycle = (List) cycles.get(i);
        for (int j = 0; j < cycle.size(); j++) {
            String node = (String) cycle.get(j);
            if (j < cycle.size() - 1) {
                System.out.print(node + " -> ");
            } else {
                System.out.print(node + " -> " + cycle.get(0));
            }
        }
        System.out.print("\n");
    }
}

leeds to the desired output of:

A -> B -> C -> A
C -> D -> C

Donald B. Johnson paper that describes the approach in more detail can be found here .


The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM