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

5

我有一个如下的Node类:

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

我需要以后序遍历的方式遍历这棵树,我正在使用Guava TreeTraverser进行遍历。Guava TreeTraverser是用来实现这个功能的工具。
    TreeTraverser<Node> treeTraverser = new TreeTraverser<Node>() {
                @Override
                public Iterable<Node> children(Node node) {
                    return node.children;
                }
            };

treeTraverser.postOrderTraversal(node);

问题在于给定的树可能存在循环依赖(意味着它可能是一个有向无环图)。如何高效地检测循环依赖?


当您访问一个以前访问过的节点时,就会发生循环。因此,请跟踪您已经访问过的节点(例如在“Set”中),如果您已经看到该节点,则报错。 - dimo414
2个回答

16

根据定义,一棵树是一个无环连通图。因此,不存在具有循环依赖的树。

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

请参阅此Q&A以获取高级循环检测算法。


3
在简单的话语中,树是一种非循环数据结构,当存在循环时,它就变成了

enter image description here

上面是一个图形的示例。您可以使用邻接表或邻接矩阵来表示它。您可以参考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]);
    }
}

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接