0

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.

Vishwa Ratna
  • 5,567
  • 5
  • 33
  • 55
Pietro Messineo
  • 777
  • 8
  • 28
  • why you have visited as well as recStack array, both are kind of doing the same thing. you only need one. I will post an answer in sometime, simple modification is required to ur code. – zenwraight Sep 19 '18 at 18:51
  • Ok thank you, waiting for the code! – Pietro Messineo Sep 19 '18 at 18:58
  • @ExceptionHandler: What are the properties of the graph you want to have the cycles for? Are the edges `bidirected` or `directed` and are multiple edges from one node to another node relevant? Also your example seems to be wrong (assuming its a directed graph), cycles would be: A->B->C->A, C->D->C and A->B->C->D->C->A – second Aug 08 '19 at 09:31
  • @second directed graph. The full graph is `A->B B->C C->D D->C C->A ` – Vishwa Ratna Aug 08 '19 at 09:33
  • @ExceptionHandler: Can you confirm the cycles I identified? Not sure whether its possible for you to update your bounty question. Also I woud assume its okay to omit permutations of the same cycle (like D->C->D) or (B->C->A->B)? – second Aug 08 '19 at 09:39
  • @second Input : `A->B B->C C->D D->C C->A ` output: two cycles: `A->B->C->A` and `C->D->C` – Vishwa Ratna Aug 08 '19 at 09:42
  • @ExceptionHandler: I interpret that as: if a cylce contains another cycle it should be omitted, same for permutations. – second Aug 08 '19 at 09:45
  • @second true, but smaller cycle should be still be printed. – Vishwa Ratna Aug 08 '19 at 10:00

1 Answers1

4

Edit: Previously I mentioned the possibility to use dfs instead of bfs, however using dfs might produce non-minimal cycles. (e.g. 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.


second
  • 4,069
  • 2
  • 9
  • 24
  • I decided to add a simplified pseudo code explanation of a brute-force algorithm. Consider reading it. – second Aug 08 '19 at 13:22
  • 3
    @ExceptionHandler Why should a valid solution be excluded? The appropriate response is "thanks, I'm trying not to use external libraries" and that's it. There's nothing in the question that states "no external libraries". – Dave Newton Aug 08 '19 at 13:27
  • 1
    That's great, also if it passed really long time! :D – Pietro Messineo Aug 09 '19 at 10:24
  • 2
    @PietroMessineo. Someone else put a `bounty` on your question. So his requirements are probably a bit different from yours. -- Just fixed an error in my pseudo code algorithm, as a pure `bfs`/`dfs` solution won't be able to identify all existing elementary cycles (however thats unrelated to the bountry question). – second Aug 09 '19 at 10:28
  • @second , really liked the way you explained. I am able to implement with external jars. Still i am not able to solve without importing the external JARs. Would love if you can solve my issue without using external JARs and keeping use of Jars that are in JDK 8. – Vishwa Ratna Aug 12 '19 at 09:36
  • @ExceptionHandler: I am not sure what qualifies as `external library` in your eyes, but If I were to post some sample implementation here, then according to the stackoverflow-terms you would still be required to put it under the `creative commons licence`. In my eyes that is the same as using a `external library`. I am sure that problem can be solved by using `JDK` resources only. but if you exclude `external libraries` you have to do it on your own. If you have specific question about the implementation feel free to ask a new question. – second Aug 12 '19 at 12:01
  • 1
    Yes I understand that point. If the licence is not an issue for you, why not copy the classes? (e.g. as inner classes, if the online IDE restricts you to one file) – second Aug 12 '19 at 12:05
  • 1
    If I undestand correctly doing that would be a violation of `stackoverflow-terms` (as its licensed by `BSD-2` and not `CC`). Unless you are unable to access the download link, I am pretty sure you can merge those 5 clases on your own. – second Aug 12 '19 at 12:11