我可以在有向图中仅使用一个简单的Set进行循环检测吗?

时间:2015-02-05 22:30:18

标签: algorithm data-structures graph

我已经看到很多算法要做循环检测,例如在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;
}

1 个答案:

答案 0 :(得分:2)

我不是java的专家,但在递归中谈论C ++传递集等并不是时间效率。在插入/删除元素时也设置 O(log(n))。你的逻辑对我来说是正确的。但是,您可以通过保留两个阵列 parent [] visited [] 来更有效,更轻松地完成此操作。基本上做bfs和跟随是伪代码(被访问被初始化为全零)。

   /* There are n nodes from 0 to n-1 */
   visited[0]=1
   parent[0]=0
   flag=0
   queue.push(0)
   while the queue is not empty
       top = queue.front()
       queue.pop()
       for all neighbors x of top
           if not visited[top]
              visited[x]=1
              parent[x]=top
              queue.push(x)
           else if visited[x] is 1 and parent[top] is not x
                flag = 1

   if flag is 1 cycle is there otherwise not

因为从0开始可能没有必要访问所有节点。所以重复直到访问所有节点。复杂度 O(E + V)略高于方法 O(E + VlogV)的复杂程度。但它写起来很简单而不是递归。