Java检测循环有向图

Dav*_*eAl 2 java recursion graph depth-first-search graph-algorithm

我目前正在尝试编写一个程序来检查有向图是否是循环的.我不确定我做错了什么(我很可能做错了所以所以请StackOverflow,告诉我我的愚蠢!).我会感谢任何帮助,因为我已经到了不知道可能出现什么问题的地步.

输入是一个邻接列表,例如:

0: 2 4
1: 2 4
2: 3 4
3: 4
4: 0 1 2 3
Run Code Online (Sandbox Code Playgroud)

(0指向2和4; 1指向2和4,依此类推......)

我的想法是检查我正在检查的节点是否为"灰色"(部分探索).如果是,则它必须是后边缘,因此是循环图.始终探索黑色边缘或交叉边缘,因此不应触发循环消息.我的目标是深度搜索

如果A - > B和B - > A,则不应触发有关循环的消息(但A - > B,B - > C,C - > A应该).

hasCycle调用hasCycleInSubgraph,它通过图的Adjency List递归调用自身.

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;
    }

}
Run Code Online (Sandbox Code Playgroud)

And*_*ner 9

你过度复杂了:可以通过深度优先搜索来检测循环:从任何给定节点,走到每个连接的节点; 如果你回到已经访问过的节点,你就有了一个周期.

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;
  }
}
Run Code Online (Sandbox Code Playgroud)

如果要检测超过给定长度的周期,只需检查递归的深度:

if (visited.contains(node) && visited.size() > 2) {
Run Code Online (Sandbox Code Playgroud)

请注意,除了堆栈中的内容之外,这不需要保留任何状态.依赖可变状态会使代码线程不安全(例如,两个线程同时调用hasCycle会相互干扰),因此应该避免 - 即使您不希望代码在多线程中使用现在,它避免了问题.