【问题标题】:How to detect circular reference in a Tree?如何检测树中的循环引用?
【发布时间】:2016-06-19 12:41:57
【问题描述】:

我有一个 Node 类如下:

public class Node{
    Object data;
    List<Node> children;
}

我需要按 post 顺序遍历这棵树,我正在使用 Guava TreeTraverser 进行相同的操作。

    TreeTraverser<Node> treeTraverser = new TreeTraverser<Node>() {
                @Override
                public Iterable<Node> children(Node node) {
                    return node.children;
                }
            };

treeTraverser.postOrderTraversal(node);

问题是给定的树有可能具有循环依赖关系(意味着它可能是循环图)。检测循环依赖的有效方法是什么?

【问题讨论】:

  • 当你访问一个你之前访问过的节点时会发生一个循环。因此,请跟踪您已经访问过的节点(例如在 Set 中),如果您已经看到该节点,则会出错。

标签: java data-structures tree guava tree-traversal


【解决方案1】:

根据定义,树是acyclic 连通图。因此,不存在循环依赖的树。

您可以通过应用深度优先遍历来查找图中的循环,并查找向下访问的节点。如果您访问在 DFS 之前的步骤中看到的节点,则该图不是一棵树。

请参阅Q&A 了解高级循环检测算法。

【讨论】:

    【解决方案2】:

    简单来说Tree is a non cyclic data structure,当有循环时,它变成Graph

    以上是图表的示例。您可以使用邻接列表或邻接矩阵来表示它。 Graphs的基础知识可以参考http://krishnalearnings.blogspot.in/2015/11/basics-of-graph-in-computer-science.html

    在上图中,我们可以将其表示如下(在您的问题中,您使用了邻接列表的表示形式)。

    int[][] graph = {   {0,1,0,0,0,0},
                        {0,0,1,0,0,0},
                        {0,0,0,1,1,0},
                        {0,0,0,0,0,0},
                        {0,0,0,0,0,1},
                        {0,1,0,0,0,0},
                    };
    

    1 表示从对应的行编号顶点到列编号顶点的边。

    我们可以编写一个简单的方法来检测循环的存在,并在图中打印任何一个循环。

    static int[] cycleElements;
    static int cycleElementIndex = 0;
    static boolean cycleFound = false;
    static final int NEW = 0;
    static final int PUSHED = 1;
    static final int POPPED = 2;
    public static int findCycle(int[][] graph,int N, int u, int[] states){
        for(int v = 0; v < N; v++){
            if(graph[u][v] == 1){
                if(states[v] == PUSHED){
                    // cycle found
                    cycleFound = true;
                    return v;
                }else if(states[v] == NEW){
                    states[v] = PUSHED;
                    int poppedVertex = findCycle(graph, N, v, states);
                    states[v] = POPPED;
                    if(cycleFound){
                        if(poppedVertex == u){
                            cycleElements[cycleElementIndex++] = v;
                            cycleElements[cycleElementIndex++] = u;
                            cycleFound = false;
                        }else{
                            cycleElements[cycleElementIndex++] = v;
                            return poppedVertex;
                        }
                    }
                }
            }
        }
        return -1;
    }
    public static void main(String[] args) {
        int N = 6;
        int[][] graph = {   {0,1,0,0,0,0},
                            {0,0,1,0,0,0},
                            {0,0,0,1,1,0},
                            {0,0,0,0,0,0},
                            {0,0,0,0,0,1},
                            {0,1,0,0,0,0},
                        };
        cycleElements = new int[N];
        int[] states = new int[N];
        states[0] = PUSHED;
        findCycle(graph,N,0,states);
        for(int i = 0; i < cycleElementIndex; i++){
            System.out.println(cycleElements[i]);
        }
    }
    

    如果您愿意,您可以轻松地将上述方法转换为邻接列表,尽管表示并不重要(与邻接矩阵相比,只有邻接列表可以节省您的空间)

    【讨论】:

      猜你喜欢
      • 2018-06-03
      • 2011-11-24
      • 2016-10-26
      • 2021-01-14
      • 1970-01-01
      • 2014-02-26
      • 2015-07-14
      • 2020-08-02
      • 2012-11-10
      相关资源
      最近更新 更多