Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Algorithm 如何确定给定的有向图是否是树_Algorithm_Tree_Directed Graph - Fatal编程技术网

Algorithm 如何确定给定的有向图是否是树

Algorithm 如何确定给定的有向图是否是树,algorithm,tree,directed-graph,Algorithm,Tree,Directed Graph,程序的输入是图形中的一组边。例如,考虑下面的简单有向图: a -> b -> c # given a graph and a starting vertex, check if the graph is a tree def checkTree(G, v): # |E| = |V| - 1 if edges.size != vertices.size - 1: return false; for v in vertices:

程序的输入是图形中的一组边。例如,考虑下面的简单有向图:

a -> b -> c
 # given a graph and a starting vertex, check if the graph is a tree
 def checkTree(G, v):

    # |E| = |V| - 1
    if edges.size != vertices.size - 1:
        return false;

    for v in vertices:
        visited[v] = false;

    hasCycle = explore_and_check_cycles(G, v);

    # a tree is acyclic
    if hasCycle:
        return false;

    for v in vertices:
        if not visited[v]: # the graph isn't connected
            return false;

    # otherwise passes all properties for a tree
    return true;

# given a Graph and a vertex, explore all reachable vertices from the vertex
# and returns true if there are any cycles
def explore_and_check_cycles(G, v):
    visited[v] = true;

    for (v, u) in edges:
        if not visited[u]:
            return explore_and_check_cyles(G, u)
        else: # a backedge between two vertices indicates a cycle
            return true

    return false
此图的边集为

{ (b, c), (a, b) }
给定一个有向图作为一组边,如何确定有向图是否是树?如果是树,那么树的根节点是什么

首先,我看的是如何表示这个图,邻接列表/邻接矩阵/其他任何东西?您将如何利用您选择的表述有效地回答上述问题

编辑1:


有些人正在考虑使用DFS进行周期检测,但问题是从哪个节点启动DFS。因为它是一个有向图,所以我们不能从随机节点启动DFS,例如,如果我从顶点“c”启动DFS,它将不会继续,因为没有后缘可以转到任何其他节点。接下来的问题应该是如何确定此树的根。

从根开始,“标记”它,然后转到所有子级并递归重复。如果你接触到一个已经被标记的孩子,这意味着它不是一棵树。

注意:这绝不是最有效的方法,但在概念上是有用的。有时你需要效率,有时你需要一个替代的观点,因为教育学的原因。这当然是后者

算法:从大小为
n
的邻接矩阵
A
开始。取矩阵幂
A**n
。如果矩阵对于每个条目都为零,那么您知道它至少是树的集合(森林)。如果可以显示它已连接,则它必须是一棵树。见a。了解更多信息

为了找到根节点,我们假设您已经显示了图是一个连通树。设
k
为矩阵变为零之前,必须提高幂次
A**k
的次数。将转置带到
(k-1)
power
A.T**(k-1)
。唯一的非零项必须是根


分析:粗略的最坏情况分析表明,它的上限为
O(n^4)
,矩阵乘法最多为
n
次。通过对矩阵进行对角化,您可以做得更好,这将使矩阵降到
O(n^3)
。考虑到时间/空间中的这个问题,这只是逻辑和理解问题的有用练习。

这里有一个相当直接的方法。它可以通过邻接矩阵或边列表来完成

  • 查找不显示为任何边的目标的节点集R。如果R没有恰好一个成员,则该图不是树

  • 如果R确实有一个成员R,那么它是唯一可能的根

  • 马克r

  • 从r开始,递归地标记从源到目标通过跟随边可以到达的所有节点。如果已经标记了任何节点,则存在一个循环,并且图形不是树。(此步骤与先前发布的答案相同)

  • 如果在步骤3结束时未标记任何节点,则该图不是树

  • 如果这些步骤都没有发现该图不是一棵树,则该图是一棵以r为根的树


    如果没有关于节点和边数的信息,很难知道什么是有效的。

    有3个属性可以检查图形是否是树:

    • (1) 图中的边数正好比顶点数| E |=| V |-1少一条
    • (2) 没有周期
    • (3) 图是连通的
    我认为这个示例算法可以在有向图的情况下工作:

    a -> b -> c
    
     # given a graph and a starting vertex, check if the graph is a tree
     def checkTree(G, v):
    
        # |E| = |V| - 1
        if edges.size != vertices.size - 1:
            return false;
    
        for v in vertices:
            visited[v] = false;
    
        hasCycle = explore_and_check_cycles(G, v);
    
        # a tree is acyclic
        if hasCycle:
            return false;
    
        for v in vertices:
            if not visited[v]: # the graph isn't connected
                return false;
    
        # otherwise passes all properties for a tree
        return true;
    
    # given a Graph and a vertex, explore all reachable vertices from the vertex
    # and returns true if there are any cycles
    def explore_and_check_cycles(G, v):
        visited[v] = true;
    
        for (v, u) in edges:
            if not visited[u]:
                return explore_and_check_cyles(G, u)
            else: # a backedge between two vertices indicates a cycle
                return true
    
        return false
    
    资料来源: S.Dasgupta、C.H.Papadimitriou和U.V.Vazirani的算法

    对于有向图,如果无向图是非循环且完全连通的,则底层无向图将是一棵树。如果对于有向图,每个顶点的阶数均为1,但其中一个顶点的阶数为0,则相同的属性适用


    如果邻接列表表示也支持每个顶点的度属性,那么我们可以很容易地应用上述规则。否则,我们应该应用一个经过调整的DFS来查找底层无向图以及| E |=| V |-1的循环

    以下是我为此编写的代码。请随意提出优化建议

    import java.util.*;
    import java.lang.*;
    import java.io.*;
    
    class Graph
    {
    private static int V;
    private static int adj[][];
    
    static  void initializeGraph(int n)
    {
        V=n+1;
        adj = new int[V][V];
        for(int i=0;i<V;i++)
        {
            for(int j=0;j<V ;j++)
            adj[i][j]= 0;
        }
    
    }
    
    static int isTree(int edges[][],int n)
    {
        initializeGraph(n);
    
        for(int i=0;i< edges.length;i++)
        {
            addDirectedEdge(edges[i][0],edges[i][1]);
        }
    
        int root = findRoot();
        if(root == -1)
        return -1;
        boolean visited[] = new boolean[V];
        boolean isTree = isTree(root, visited, -1);
        boolean isConnected = isConnected(visited);
        System.out.println("isTree= "+ isTree + " isConnected= "+ isConnected);
        if(isTree && isConnected)
        return root;
        else 
        return -1;
    
    }
    
    static  boolean isTree(int node, boolean visited[], int parent)
    {
    //  System.out.println("node =" +node +" parent" +parent);
        visited[node] = true;int j;
    
        for(j =1;j<V;j++)
        {
        //  System.out.println("node =" +node + " j=" +j+ "parent" + parent);
            if(adj[node][j]==1)
            {
                if(visited[j])
                {
                //  System.out.println("returning false for j="+ j);
                    return false;
                }
                else
                {   //visit all adjacent vertices
                    boolean child = isTree(j, visited, node);
                    if(!child)
                    {
                    //  System.out.println("returning false for j="+ j + " node=" +node);
                        return false;   
                    }
                }
    
            }
        }
        if(j==V)
        return true;
        else
        return false;
    }
    
    static int findRoot()
    {
        int root =-1, j=0,i;
        int count =0;
        for(j=1;j<V ;j++)
        {
            count=0;
            for(i=1 ;i<V;i++)
            {
                if(adj[i][j]==1)
                count++;
            }
        //  System.out.println("j"+j +" count="+count);
            if(count==0)
            {
            //  System.out.println(j);
                return j;   
            }
        }
        return -1;
    }
    
    static void addDirectedEdge(int s, int d)
    {
    //  System.out.println("s="+ s+"d="+d);
        adj[s][d]=1;
    }
    
    static boolean isConnected(boolean visited[])
    {
        for(int i=1; i<V;i++)
        {
            if(!visited[i])
            return false;
        }
        return true;
    }
    
    public static void main (String[] args) throws java.lang.Exception
    {
        int edges[][]= {{2,3},{2,4},{3,1},{3,5},{3,7},{4,6}, {2,8}, {8,9}};
        int n=9;
        int root = isTree(edges,n);
        System.out.println("root is:" + root);
    
        int edges2[][]= {{2,3},{2,4},{3,1},{3,5},{3,7},{4,6}, {2,8}, {6,3}};
        int n2=8;
        root = isTree(edges2,n2);
        System.out.println("root is:" + root);
       }
    }
    
    import java.util.*;
    导入java.lang.*;
    导入java.io.*;
    类图
    {
    专用静态INTV;
    私有静态int adj[][];
    静态无效初始化图(int n)
    {
    V=n+1;
    adj=新整数[V][V];
    
    对于(int i=0;i家庭作业?面试问题?可能只是一个注释的重复:由于原始图形是定向的,要使用人们在下面描述的算法,您需要将边视为无向/双向。如何“从根开始”?您没有获得根。嗯……您需要找到一个没有父节点的节点,并将其称为“根”…此外,您还需要确保标记所有节点,因为它可能不是树而是林…实际上,如果您考虑它,您可以从任何节点开始,只要您确保标记所有节点…此外,如果终止时仍有未访问的顶点,则图形具有多个连接组件,因此不是树(虽然它可能是一片森林…)幂零并不意味着“全零”。如果
    A**k=0
    ,那么
    A
    本身就是。@TedHopp我同意,但矩阵幂零的概念与图中循环的存在有关。为了清晰起见,我将对其进行编辑。我在考虑类似的问题。我使用邻接列表来存储图,因为我可以一次处理一条边并构建列表。我想知道……是吗足够验证属性1和3了吗?我没有给出一个具有属性1和3并且仍然是循环的图的例子。。。