A cycle is a path that returns to the same vertex. Sometimes a cycle is normal (a road network), sometimes it is a bug: circular package dependencies, processes blocking each other (a deadlock), a task order, a linked list closed into a ring. How to find it depends on the kind of graph.
Undirected graph
With DFS: an edge to an already visited vertex that is not the parent means a cycle. The parent is excluded because an undirected edge goes both ways. (See DFS.)
def has_cycle(graph):
seen = set()
def dfs(node, parent):
seen.add(node)
for nxt in graph[node]:
if nxt == parent:
continue # the edge we came by is not a cycle
if nxt in seen or dfs(nxt, node):
return True
return False
return any(node not in seen and dfs(node, None) for node in graph)
tree = {1: [2, 3], 2: [1], 3: [1, 4], 4: [3]}
loop = {1: [2, 3], 2: [1, 3], 3: [1, 2, 4], 4: [3]}
print("tree:", has_cycle(tree))
print("loop:", has_cycle(loop))tree: False
loop: TrueWith Union-Find: add the edges one by one; if both ends are already in one set, this edge closes a cycle. Handy when edges arrive as a stream, and the basis of Kruskal. See Union-Find.