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

时间:2012-11-16 02:05:53

标签: algorithm tree directed-graph

程序的输入是图中的一组边。对于例如考虑以下简单有向图:

a -> b -> c

此图的边集是

{ (b, c), (a, b) }

因此,将有向图作为一组边,如何确定有向图是否为树?如果它是树,那么树的根节点是什么?

首先,我在看你如何表示这个图,邻接列表/邻接矩阵/其他什么东西?如何利用您所选择的表达方式有效地回答上述问题?

编辑1:

有些人正在考虑使用DFS进行循环检测,但问题是从哪个节点启动DFS。由于它是有向图,我们不能从随机节点启动DFS,例如,如果我从顶点'c'开始DFS,它将不会继续进行,因为没有后边缘去任何其他节点。这里的跟进问题应该是如何确定这棵树的根源。

6 个答案:

答案 0 :(得分:8)

这是一个相当直接的方法。它可以使用邻接矩阵或边缘列表来完成。

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

  2. 如果R确实只有一个成员r,则它是唯一可能的根。

  3. Mark r。

  4. 从r开始,递归地标记从源到目标的后续边缘可以到达的所有节点。如果已标记任何节点,则存在循环,并且图形不是树。 (此步骤与之前发布的答案相同)。

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

  6. 如果这些步骤都没有发现图形不是树,则图形是以r为根的树。

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

答案 1 :(得分:4)

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

  • (1)图中的边数正好比顶点数| E |小1 = | V | - 1
  • (2)没有周期
  • (3)图表已连接

我认为这个示例算法可以在有向图的情况下工作:

 # 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.瓦齐拉尼 http://www.cs.berkeley.edu/~vazirani/algorithms.html

答案 2 :(得分:3)

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

答案 3 :(得分:1)

注意:绝不是最有效的方式,但在概念上很有用。有时候你需要效率,有时你会因为教学原因而想要另一种观点。这肯定是后来的。

算法:从大小为A的邻接矩阵n开始。取矩阵幂A**n。如果每个条目的矩阵为零,则您知道它至少是一组树(森林)。如果您可以证明它已连接,则它必须是树。查看Nilpotent matrix。了解更多信息。

要查找根节点,我们假设您已经显示图形是连接的树。设k为矩阵变为零之前必须提高幂A**k的次数。将转置设为(k-1)A.T ** (k-1)。唯一的非零条目必须是根。

分析:粗略的案例分析表明它的上限为O(n^4),矩阵乘法最多为n次。您可以通过对齐矩阵来做得更好,这应该将其降低到O(n^3)。考虑到O(n), O(1)时间/空间中的这个问题can be done,这只是对逻辑和理解问题的有用练习。

答案 4 :(得分:1)

对于有向图,如果无向图是非循环的并且完全连接,则基础无向图将是树。如果对于有向图,每个顶点具有in-degree = 1,除了具有in-degree = 0的顶点之外,相同的属性保持良好。

如果邻接列表表示还支持每个顶点的in-degree属性,那么我们可以轻松应用上述规则。否则,我们应该应用一个经过调整的DFS来查找基础无向图的无循环以及| E | = | V | -1。

答案 5 :(得分: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);
   }
}