程序的输入是图中的一组边。对于例如考虑以下简单有向图:
a -> b -> c
此图的边集是
{ (b, c), (a, b) }
因此,将有向图作为一组边,如何确定有向图是否为树?如果它是树,那么树的根节点是什么?
首先,我在看你如何表示这个图,邻接列表/邻接矩阵/其他什么东西?如何利用您所选择的表达方式有效地回答上述问题?
编辑1:
有些人正在考虑使用DFS进行循环检测,但问题是从哪个节点启动DFS。由于它是有向图,我们不能从随机节点启动DFS,例如,如果我从顶点'c'开始DFS,它将不会继续进行,因为没有后边缘去任何其他节点。这里的跟进问题应该是如何确定这棵树的根源。
答案 0 :(得分:8)
这是一个相当直接的方法。它可以使用邻接矩阵或边缘列表来完成。
查找未显示为任何边的目标的节点集R.如果R没有恰好一个成员,则图表不是树。
如果R确实只有一个成员r,则它是唯一可能的根。
Mark r。
从r开始,递归地标记从源到目标的后续边缘可以到达的所有节点。如果已标记任何节点,则存在循环,并且图形不是树。 (此步骤与之前发布的答案相同)。
如果在步骤3结束时未标记任何节点,则该图形不是树。
如果这些步骤都没有发现图形不是树,则图形是以r为根的树。
如果没有关于节点数和边数的一些信息,很难知道什么是有效的。
答案 1 :(得分:4)
有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);
}
}