【问题标题】:Java Detecting a cyclic directed GraphJava检测循环有向图
【发布时间】:2016-04-24 19:07:49
【问题描述】:

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

输入是邻接表,如:

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

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

}

【问题讨论】:

    标签: java recursion graph graph-algorithm depth-first-search


    【解决方案1】:

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

    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 (visited.contains(node) && visited.size() > 2) {
    

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

    【讨论】:

    • 你确定这是正确的吗?如果一个图从节点 A 开始,分支成两条路径然后收敛怎么办?没有循环,但您的代码不会在第二次访问时将节点识别为循环吗?
    • 我假设你指的是像0-&gt;1; 0-&gt;2; 1-&gt;3; 2-&gt;3; 3-&gt;4; 3-&gt;5这样的例子?那里没有问题:从0 出发的每条路径的行走都是单独考虑的。
    • 是的!好的,所以访问的节点是当前路径上的一个列表。明白了
    • 正确的方法是visited.size()(不是visited.length())
    • 这是针对无向图而不是有向图的吧?
    猜你喜欢
    • 2017-01-19
    • 1970-01-01
    • 1970-01-01
    • 2016-03-28
    • 1970-01-01
    • 1970-01-01
    • 2013-12-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多