如何使用递归 DFS 查找图是不是包含循环?
Posted
技术标签:
【中文标题】如何使用递归 DFS 查找图是不是包含循环?【英文标题】:How to find if a graph contains a cycle using a recursive DFS?如何使用递归 DFS 查找图是否包含循环? 【发布时间】:2017-10-19 21:38:39 【问题描述】:下面的代码是深度优先的实现
搜索 DFS 以确定有向图是否有环。但是,它似乎有一个错误,因为它不起作用。我几乎 100% 确定该错误存在于if (visited[w])
条件下。我的逻辑基本上是——如果一个节点已经被访问过,那么就存在一个循环。但是,if (visited[w])
的问题在于,虽然条件可能为真,但并不一定意味着存在一个循环,因为该节点可能早就被访问过。
int *visited; // array [0..V-1] of booleans
int checkCycle(Graph g)
visited = malloc(sizeof(int)*g->numVertices);
int result = dfsCycleCheck(g, 0);
free(visited);
return result;
int dfsCycleCheck(Graph g, Vertex v)
visited[v] = 1;
Vertex w;
for (w = 0; w < nV(g); w++)
if (!hasEdge(g,v,w)) continue;
if (visited[w]) return 1; // found cycle
return dfsCycleCheck(g, w);
return 0; // no cycle
【问题讨论】:
malloc()
不会初始化它提供给你的内存,你也不会自己初始化它。谁知道里面有什么?如果您希望它以全零开始,那么最简单的解决方案是使用 calloc()
而不是 malloc()
进行分配。
@JohnBollinger 我使用 for 循环将所有内容初始化为 0(上面未显示),但这显然不是问题
另外,您从节点 0 开始执行 DFS,但如果您的图表未连接,那么您可能会错过从该节点无法访问的循环。
目前尚不清楚您的图是否有有向边,但如果有,那么这也将考虑在内,这使得您更有可能因为您选择的起始节点而错过一个循环,并且更棘手来解决这个问题。
@JohnBollinger 该图始终是连接的(即所有节点都可以访问 - 没有孤立的节点)。至于有向/无向,我会说它是一个有向图,但无向的解决方案也很好
【参考方案1】:
您是正确的,没有办法判断被访问的节点是否已经被访问过,或者是否作为当前遍历的一部分被访问过。
一种方法是维护一个顶点数组,该数组可以保持三种状态,而不是我们已经拥有的两种状态。
WHITE :尚未处理顶点。最初 所有顶点都是白色的。
GRAY :正在处理顶点(DFS 用于此 顶点已开始,但尚未完成,这意味着 该顶点的所有后代(ind DFS 树) 尚未处理(或此顶点在功能 调用栈)
BLACK :顶点及其所有后代都是 已处理。
在进行 DFS 时,如果我们遇到从当前顶点到 灰色顶点,则这条边是后边,因此有一个循环。
代码将是这样的。
// Recursive function to find if there is back edge // in DFS subtree tree rooted with 'u' bool Graph::DFSUtil(int u, int color[]) // GRAY : This vertex is being processed (DFS // for this vertex has started, but not // ended (or this vertex is in function // call stack) color[u] = GRAY; // Iterate through all adjacent vertices list<int>::iterator i; for (i = adj[u].begin(); i != adj[u].end(); ++i) int v = *i; // An adjacent of u // If there is if (color[v] == GRAY) return true; // If v is not processed and there is a back // edge in subtree rooted with v if (color[v] == WHITE && DFSUtil(v, color)) return true; // Mark this vertex as processed color[u] = BLACK; return false; // Returns true if there is a cycle in graph bool Graph::isCyclic() // Initialize color of all vertices as WHITE int *color = new int[V]; for (int i = 0; i < V; i++) color[i] = WHITE; // Do a DFS traversal beginning with all // vertices for (int i = 0; i < V; i++) if (color[i] == WHITE) if (DFSUtil(i, color) == true) return true; return false;
这里的主要区别是节点可以被访问并且仍然是黑色的(表示该节点之前被访问过)或灰色(表示该节点作为当前遍历的一部分被访问;所以它是一个后边缘)帮助我们看看我们是否有一个周期。
这在之前是不可能的,因为我们没有区分两种类型的访问节点的布尔数组。
Source
【讨论】:
以上是关于如何使用递归 DFS 查找图是不是包含循环?的主要内容,如果未能解决你的问题,请参考以下文章