我如何检查有向图是否为无环图?该算法名称是什么?如果有参考资料将不胜感激。
我如何检查有向图是否为无环图?该算法名称是什么?如果有参考资料将不胜感激。
仅进行简单的深度优先搜索(DFS)无法很好地找到环。在DFS中,可能会多次访问一个节点而不存在环。而且,根据开始的位置,您也可能不会访问整个图。
您可以按如下方式检查图的连接组件中是否存在环:找到仅具有出边的节点。如果没有这样的节点,则存在环。从该节点开始进行DFS。在遍历每条边时,检查边是否指向已经在您的堆栈上的节点。这表明存在环。如果找不到这样的边,则该连接组件中没有环。
正如Rutger Prins所指出的那样,如果您的图不是连通的,则需要在每个连接组件上重复搜索。
作为参考,Tarjan强连通分量算法与此密切相关。它还将帮助您找到环,而不仅仅是报告它们是否存在。
引理22.11在书籍
算法导论
(第二版)中说明:如果有向图G是无环的,则对G进行深度优先搜索不会产生后向边。
解决方案1:使用Kahn算法检测循环。主要思想:维护一个队列,在队列中加入入度为0的节点。然后逐个取出节点,直到队列为空。检查是否存在任何节点的入边。
解决方案2:使用Tarjan算法检查强连通分量。
解决方案3:使用深度优先搜索(DFS)。使用整数数组标记节点的当前状态: 即0——表示此节点以前未被访问。 -1——表示此节点已被访问,正在访问其子节点。 1——表示此节点已被访问,并已完成。 因此,在进行DFS时,如果节点的状态为-1,则意味着必定存在一个循环。
我在 Google 的面试中遇到了这个问题。
你可以尝试使用拓扑排序,其时间复杂度为 O(V + E),其中 V 是顶点的数量,E 是边的数量。一个有向无环图当且仅当可以进行拓扑排序时才是无环的。
递归删除叶子节点,直到没有节点剩余,如果剩余超过一个节点,则存在一个环路。除非我理解错了,否则时间复杂度为 O(V^2 + VE)。
然而,一种高效的类 DFS 算法,最坏情况下的时间复杂度为 O(V + E):
function isAcyclic (root) {
const previous = new Set();
function DFS (node) {
previous.add(node);
let isAcyclic = true;
for (let child of children) {
if (previous.has(node) || DFS(child)) {
isAcyclic = false;
break;
}
}
previous.delete(node);
return isAcyclic;
}
return DFS(root);
}
def isDAG(nodes V):
while there is an unvisited node v in V:
bool cycleFound = dfs(v)
if cyclefound:
return false
return true
这个程序的时间复杂度为O(n+m)或O(n^2)
m=n^2
条边,所以 m = O(n^2)
。因此,O(n+m) = O(n + n^2) = O(n^2)
。 - Alex Reinking这是一个用Swift编写的代码,用于查找图中是否有循环:
func isCyclic(G : Dictionary<Int,Array<Int>>,root : Int , var visited : Array<Bool>,var breadCrumb : Array<Bool>)-> Bool
{
if(breadCrumb[root] == true)
{
return true;
}
if(visited[root] == true)
{
return false;
}
visited[root] = true;
breadCrumb[root] = true;
if(G[root] != nil)
{
for child : Int in G[root]!
{
if(isCyclic(G,root : child,visited : visited,breadCrumb : breadCrumb))
{
return true;
}
}
}
breadCrumb[root] = false;
return false;
}
let G = [0:[1,2,3],1:[4,5,6],2:[3,7,6],3:[5,7,8],5:[2]];
var visited = [false,false,false,false,false,false,false,false,false];
var breadCrumb = [false,false,false,false,false,false,false,false,false];
var isthereCycles = isCyclic(G,root : 0, visited : visited, breadCrumb : breadCrumb)
在检查给定节点下面的循环时,只需将该节点与空哈希集一起传递即可
private bool FindCycle(int node, HashSet<int> path)
{
if (path.Contains(node))
return true;
var extendedPath = new HashSet<int>(path) {node};
foreach (var child in GetChildren(node))
{
if (FindCycle(child, extendedPath))
return true;
}
return false;
}
Here my implementation in pseudocode:
bool Acyclacity_Test
InitColor() //Sets to WHITE every vertex
while there is a node v in V:
if (v.color == WHITE) then
tmp = Aux_Acy(v);
if ( not tmp ) return false
return true
END
bool Aux_Acy(u)
u.color = GREY
for each node v in Adj(u)
if(v.color == GREY) return false
else if(v.color == WHITE) tmp = Aux_Acy(v)
if(!tmp) return false;
u.color = BLACK
return true
END