在有向图中检测循环
给定一个有向图,检查该图是否包含循环。如果给定的图形包含至少一个循环,您的函数应该返回 true,否则返回 false。
例子,
Input: n = 4, e = 6
0 -> 1, 0 -> 2, 1 -> 2, 2 -> 0, 2 -> 3, 3 -> 3
Output: Yes
Explanation:
Diagram:
The diagram clearly shows a cycle 0 -> 2 -> 0
Input:n = 4, e = 4
0 -> 1, 0 -> 2, 1 -> 2, 2 -> 3
Output:No
Explanation:
Diagram:
The diagram clearly shows no cycle
使用深度优先搜索或 DFS的解决方案
- 方法:深度优先遍历可用于检测图中的循环。连通图的 DFS 生成一棵树。仅当图中存在后边时,图中才存在循环。后边是从节点到自身(自循环)或其在 DFS 生成的树中的祖先之一的边。在下图中,有 3 个后边,用叉号标记。我们可以观察到这 3 个后边表示图中存在 3 个循环。
- 对于断开连接的图,获取 DFS 森林作为输出。要检测循环,请通过检查后边缘来检查单个树中的循环。
要检测后边缘,请跟踪当前在 DFS 遍历函数的递归堆栈中的顶点。如果到达已经在递归堆栈中的顶点,则树中有一个循环。将当前顶点连接到递归堆栈中的顶点的边是后边。使用recStack[]数组来跟踪递归堆栈中的顶点。
上述方法的空运行:
在上图中有一个错误节点 1 正在向 2 做有向边而不是 0 请注意。
- 算法:
- 使用给定数量的边和顶点创建图形。
- 创建一个递归函数,用于初始化当前索引或顶点、已访问和递归堆栈。
- 将当前节点标记为已访问,并在递归堆栈中标记索引。
- 找到所有未访问且与当前节点相邻的顶点。递归调用这些顶点的函数,如果递归函数返回true,则返回true。
- 如果相邻顶点已经在递归堆栈中被标记,则返回 true。
- 创建一个包装类,它为所有顶点调用递归函数,如果任何函数返回 true,则返回 true。否则,如果对于所有顶点,该函数返回 false,则返回 false。
执行:
C++
// A C++ Program to detect cycle in a graph
#include
using namespace std;
class Graph
{
int V; // No. of vertices
list *adj; // Pointer to an array containing adjacency lists
bool isCyclicUtil(int v, bool visited[], bool *rs); // used by isCyclic()
public:
Graph(int V); // Constructor
void addEdge(int v, int w); // to add an edge to graph
bool isCyclic(); // returns true if there is a cycle in this graph
};
Graph::Graph(int V)
{
this->V = V;
adj = new list[V];
}
void Graph::addEdge(int v, int w)
{
adj[v].push_back(w); // Add w to v’s list.
}
// This function is a variation of DFSUtil() in https://www.geeksforgeeks.org/archives/18212
bool Graph::isCyclicUtil(int v, bool visited[], bool *recStack)
{
if(visited[v] == false)
{
// Mark the current node as visited and part of recursion stack
visited[v] = true;
recStack[v] = true;
// Recur for all the vertices adjacent to this vertex
list::iterator i;
for(i = adj[v].begin(); i != adj[v].end(); ++i)
{
if ( !visited[*i] && isCyclicUtil(*i, visited, recStack) )
return true;
else if (recStack[*i])
return true;
}
}
recStack[v] = false; // remove the vertex from recursion stack
return false;
}
// Returns true if the graph contains a cycle, else false.
// This function is a variation of DFS() in https://www.geeksforgeeks.org/archives/18212
bool Graph::isCyclic()
{
// Mark all the vertices as not visited and not part of recursion
// stack
bool *visited = new bool[V];
bool *recStack = new bool[V];
for(int i = 0; i < V; i++)
{
visited[i] = false;
recStack[i] = false;
}
// Call the recursive helper function to detect cycle in different
// DFS trees
for(int i = 0; i < V; i++)
if ( !visited[i] && isCyclicUtil(i, visited, recStack))
return true;
return false;
}
int main()
{
// Create a graph given in the above diagram
Graph g(4);
g.addEdge(0, 1);
g.addEdge(0, 2);
g.addEdge(1, 2);
g.addEdge(2, 0);
g.addEdge(2, 3);
g.addEdge(3, 3);
if(g.isCyclic())
cout << "Graph contains cycle";
else
cout << "Graph doesn't contain cycle";
return 0;
}
Java
// A Java Program to detect cycle in a graph
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
class Graph {
private final int V;
private final List> adj;
public Graph(int V)
{
this.V = V;
adj = new ArrayList<>(V);
for (int i = 0; i < V; i++)
adj.add(new LinkedList<>());
}
// This function is a variation of DFSUtil() in
// https://www.geeksforgeeks.org/archives/18212
private boolean isCyclicUtil(int i, boolean[] visited,
boolean[] recStack)
{
// Mark the current node as visited and
// part of recursion stack
if (recStack[i])
return true;
if (visited[i])
return false;
visited[i] = true;
recStack[i] = true;
List children = adj.get(i);
for (Integer c: children)
if (isCyclicUtil(c, visited, recStack))
return true;
recStack[i] = false;
return false;
}
private void addEdge(int source, int dest) {
adj.get(source).add(dest);
}
// Returns true if the graph contains a
// cycle, else false.
// This function is a variation of DFS() in
// https://www.geeksforgeeks.org/archives/18212
private boolean isCyclic()
{
// Mark all the vertices as not visited and
// not part of recursion stack
boolean[] visited = new boolean[V];
boolean[] recStack = new boolean[V];
// Call the recursive helper function to
// detect cycle in different DFS trees
for (int i = 0; i < V; i++)
if (isCyclicUtil(i, visited, recStack))
return true;
return false;
}
// Driver code
public static void main(String[] args)
{
Graph graph = new Graph(4);
graph.addEdge(0, 1);
graph.addEdge(0, 2);
graph.addEdge(1, 2);
graph.addEdge(2, 0);
graph.addEdge(2, 3);
graph.addEdge(3, 3);
if(graph.isCyclic())
System.out.println("Graph contains cycle");
else
System.out.println("Graph doesn't "
+ "contain cycle");
}
}
// This code is contributed by Sagar Shah.
Python
# Python program to detect cycle
# in a graph
from collections import defaultdict
class Graph():
def __init__(self,vertices):
self.graph = defaultdict(list)
self.V = vertices
def addEdge(self,u,v):
self.graph[u].append(v)
def isCyclicUtil(self, v, visited, recStack):
# Mark current node as visited and
# adds to recursion stack
visited[v] = True
recStack[v] = True
# Recur for all neighbours
# if any neighbour is visited and in
# recStack then graph is cyclic
for neighbour in self.graph[v]:
if visited[neighbour] == False:
if self.isCyclicUtil(neighbour, visited, recStack) == True:
return True
elif recStack[neighbour] == True:
return True
# The node needs to be popped from
# recursion stack before function ends
recStack[v] = False
return False
# Returns true if graph is cyclic else false
def isCyclic(self):
visited = [False] * (self.V + 1)
recStack = [False] * (self.V + 1)
for node in range(self.V):
if visited[node] == False:
if self.isCyclicUtil(node,visited,recStack) == True:
return True
return False
g = Graph(4)
g.addEdge(0, 1)
g.addEdge(0, 2)
g.addEdge(1, 2)
g.addEdge(2, 0)
g.addEdge(2, 3)
g.addEdge(3, 3)
if g.isCyclic() == 1:
print "Graph has a cycle"
else:
print "Graph has no cycle"
# Thanks to Divyanshu Mehta for contributing this code
C#
// A C# Program to detect cycle in a graph
using System;
using System.Collections.Generic;
public class Graph {
private readonly int V;
private readonly List> adj;
public Graph(int V)
{
this.V = V;
adj = new List>(V);
for (int i = 0; i < V; i++)
adj.Add(new List());
}
// This function is a variation of DFSUtil() in
// https://www.geeksforgeeks.org/archives/18212
private bool isCyclicUtil(int i, bool[] visited,
bool[] recStack)
{
// Mark the current node as visited and
// part of recursion stack
if (recStack[i])
return true;
if (visited[i])
return false;
visited[i] = true;
recStack[i] = true;
List children = adj[i];
foreach (int c in children)
if (isCyclicUtil(c, visited, recStack))
return true;
recStack[i] = false;
return false;
}
private void addEdge(int sou, int dest) {
adj[sou].Add(dest);
}
// Returns true if the graph contains a
// cycle, else false.
// This function is a variation of DFS() in
// https://www.geeksforgeeks.org/archives/18212
private bool isCyclic()
{
// Mark all the vertices as not visited and
// not part of recursion stack
bool[] visited = new bool[V];
bool[] recStack = new bool[V];
// Call the recursive helper function to
// detect cycle in different DFS trees
for (int i = 0; i < V; i++)
if (isCyclicUtil(i, visited, recStack))
return true;
return false;
}
// Driver code
public static void Main(String[] args)
{
Graph graph = new Graph(4);
graph.addEdge(0, 1);
graph.addEdge(0, 2);
graph.addEdge(1, 2);
graph.addEdge(2, 0);
graph.addEdge(2, 3);
graph.addEdge(3, 3);
if(graph.isCyclic())
Console.WriteLine("Graph contains cycle");
else
Console.WriteLine("Graph doesn't "
+ "contain cycle");
}
}
// This code contributed by Rajput-Ji
Javascript
输出:
Graph contains cycle
- 复杂性分析:
- 时间复杂度: O(V+E)。
该方法的时间复杂度与 DFS 遍历的时间复杂度相同,为 O(V+E)。 - 空间复杂度: O(V)。
存储访问和递归堆栈需要 O(V) 空间。
- 时间复杂度: O(V+E)。