如何检测树中的循环引用?

How to detect circular reference in a Tree?

我有一个节点 class 如下:

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

要注意的是,给定的树有可能具有循环依赖性(意味着它可能是循环图)。什么是检测循环依赖的有效方法?

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

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

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

简单来说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]);
    }
}

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