【发布时间】:2015-02-05 22:30:17
【问题描述】:
我见过相当多的算法来做循环检测,比如Tarjan's strongly connected components algorithm在Best algorithm for detecting cycles in a directed graph中的回答。
我从没想过检测周期会那么复杂,我一直相信一个简单的Set 可以帮助解决问题。
因此,除了通常的marker 数组来记录访问过的顶点外,我们还可以使用额外的Set 来记录从源开始沿路径的所有顶点。
关键是记住在所有下一个邻居都完成后从集合中删除一个顶点。
一个简单的代码是这样的:
public boolean hasCycle(List<Integer>[] vs) {
boolean[] marker = new boolean[v.length];
Set<Integer> tracker = new HashSet<Integer>();
for(int v = 0;v < vs.length;v++)
if (explore(v, vs, marker, tracker)) return true;
return false;
}
private boolean explore(int v, List<Integer>[] vs, boolean[] marker, Set<Integer> tracker) {
if (tracker.contains(v)) return true;
else if (!marker[v]) {
marker[v] = true;
tracker.add(v); // add current vertex to tracker
for (int u:vs[v]) if (explore(v, vs, marker, tracker)) return true;
tracker.remove(v); // remove the vertex from tracker, as it is fully done.
}
else return false;
}
这个算法有什么问题吗?
在 sasha 的回答的帮助下,实际上即使是集合也不是必需的,只需要一个数组就足够了。
public boolean hasCycle(List<Integer>[] vs) {
boolean[] marker = new boolean[v.length];
boolean[] onPath = new boolean[v.length];
for(int v = 0;v < vs.length;v++)
if (explore(v, vs, marker, onPath)) return true;
return false;
}
private boolean explore(int v, List<Integer>[] vs, boolean[] marker, boolean[] onPath) {
if (onPath[v]) return true;
else if (!marker[v]) {
marker[v] = true;
onPath[v] = true; // add current vertex to the path
for (int u:vs[v]) if (explore(v, vs, marker, onPath)) return true;
onPath[v] = false; // remove the vertex from the path, as it is fully done.
}
else return false;
}
【问题讨论】:
-
你是对的,简单的 DFS 足以检测图中的循环,就像你做的那样:检查后边。 Tarjan 的算法实际上计算了更复杂的东西
-
@NiklasB。但是这段代码或使用设置正确吗?
-
每个循环都会在深度优先树中产生一个后沿,这就是您要检查的内容。对我来说看起来不错,但当然可能存在我忽略的细微实现错误。
-
有些人更喜欢使用字节数组并使用 3 个不同的值来编码未访问/在根路径上/已访问的状态,而不是使用两个数组。这样会更节省空间。
-
@NiklasB。似乎是个好主意谢谢:)
标签: algorithm data-structures graph