简体   繁体   中英

Java Detecting a cyclic directed Graph

I am currently trying to write a procedure to check whether a directed graph is cyclic or not . I am not sure what i did wrong (it may be well possible that I did everything wrong, so please StackOverflow, show me my stupidity!). I'd be thankful for any kind of help as I've come to the point where I don't know what could be the problem.

The input is an adjacency list such as:

0: 2 4
1: 2 4
2: 3 4
3: 4
4: 0 1 2 3

(0 directs to 2 and 4; 1 directs to 2 and 4 and so on...)

The idea is that I check whether the node I am checking is 'grey' (partially explored) or not. If it is, it must be a back edge and thus a cyclic graph. Black edges are always explored or cross-edges, so this shouldn't trigger a cyclic message. I am aiming to do depth first search

If A-->B and B-->A, this should not trigger a message about cyclic (but A--> B, B-->C, C-->A should).

hasCycle calls hasCycleInSubgraph which calls itself recursively through the Adjency List of the Graph.

class qs {

    private ArrayList<Integer>[] adjList;

    private Stack<Integer> stack;

    private ArrayList<Integer> whiteHat;
    private ArrayList<Integer> greyHat;
    private ArrayList<Integer> blackHat;

    public qs(ArrayList<Integer>[] graph) {
        this.adjList = graph;

        this.stack = new Stack();

        this.whiteHat = new ArrayList<Integer>();
        this.greyHat = new ArrayList<Integer>();
        this.blackHat = new ArrayList<Integer>();   

        for (Integer h = 0; h < adjList.length; h++) {
            whiteHat.add(h);
        }

    }

    public boolean hasCycle() {
        for (Integer i = 0; i < adjList.length; i++) {

//          System.out.print("Local root is: ");
//          System.out.println(i);

            whiteHat.remove(i);
            greyHat.add(i);

            if (hasCycleInSubgraph(i) == true) {
                return true;
            }

            greyHat.remove(i);
            blackHat.add(i);

        }
        return false;
    }

    public boolean hasCycleInSubgraph(Integer inp) {

        if (blackHat.contains(inp)) {
            return false;
        }

        for (Integer branch : adjList[inp]) {

//          System.out.print("Adj is: ");
//          System.out.println(branch);

            if ( greyHat.contains(branch) && !inp.equals(branch) ) {
                return true;
            }

            whiteHat.remove(branch);
            greyHat.add(branch);

            if ( hasCycleInSubgraph(branch) == true ) {
                return true;
            }   

            greyHat.remove(branch);
            blackHat.add(branch);

        }
        return false;
    }

}

You are over-complicating it: a cycle can be detected via a depth-first search: from any given node, walk to each of the connected nodes; if you arrive back at an already-visited node, you've got a cycle.

class qs {
  private final ArrayList<Integer>[] graph;

  qs(ArrayList<Integer>[] graph) {
    this.graph = graph;
  }

  boolean hasCycle() {
    List<Integer> visited = new ArrayList<>();
    for (int i = 0; i < graph.length; ++i) {
      if (hasCycle(i, visited)) {
        return true;
      }
    }
  }

  private boolean hasCycle(int node, List<Integer> visited) {
    if (visited.contains(node)) {
      return true;
    }
    visited.add(node);
    for (Integer nextNode : graph[node]) {
      if (hasCycle(nextNode, visited)) {
        return true;
      }
    }
    visited.remove(visited.length() - 1);
    return false;
  }
}

If you want to detect cycles longer than a given length, just check the depth of the recursion:

if (visited.contains(node) && visited.size() > 2) {

Note that this does not require any state to be kept, aside from what is in the stack. Relying upon mutable state makes the code thread-unsafe (eg that two threads calling hasCycle at the same time would interfer with each other), and so should be avoided - even if you don't expect the code to be used in a multi-threaded way now, it avoids problems down the line.

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