I have implemented this code using dfs to detect if there is a cycle in a graph, and if there is, also print the vertices of this cycle. If there is more than one cycle, just print the first one that you've found. But somehow the OJ told me it's not efficient enough for some test cases. Any ideas about how to improve the efficiency of this piece of code?
I've been thinking hard about how to improve this, but no progress. I guess maybe I should try to use another algorithm other than dfs?
# using python3
from collections import defaultdict
class Graph():
def __init__(self, V):
self.V = V
self.graph = defaultdict(list)
def add_edge(self, u, v):
self.graph[u].append(v)
def dfs_walk(self, u):
# List to contain the elements of a circle
list_circle = list()
# Mark visited vertexes
visited = list()
stack = [u]
while stack:
v = stack.pop()
visited.append(v)
# If already in list_circle, means there is a circle.
if v in list_circle:
return True, list_circle[list_circle.index(v):], visited
# If v is not in list_circle and it has neighbor, collect it in the list,
# go to next vertex. If it hasn't neighbor, check the left vertex
else:
# the next vertex is the first neighbor of this vertex
if len(self.graph[v]) > 0:
stack.extend(self.graph[v])
list_circle.append(v)
# Didn't find a circle in this round.
return False, list_circle, visited
def is_cyclic(self):
control = [-1] * self.V
for i in range(self.V):
if control[i] == -1:
flag, list_circle, visited = self.dfs_walk(i)
for x in visited:
control[x] = 0
if flag:
return True, list_circle
# Didn't find any circle in all rounds.
return False, list_circle
if __name__ == "__main__":
line = input().split()
V, E = int(line[0]), int(line[1])
# Initialize the graph
g = Graph(V)
for r in range(E):
row = input().split()
start, end = int(row[0])-1, int(row[1])-1
g.add_edge(start, end)
flag, list_circle = g.is_cyclic()
if flag:
print("YES")
print(" ".join(str(i+1) for i in list_circle))
else:
print("NO")
The first line is the number of vertices and number of edges. and after the first line, each line represents an edge (directed).
Input:
3 3
1 2
2 3
3 1
Output:
YES
1 2 3
I'd say this code is not very inefficient. Why do you have list_circle
and visited
as different things? Also storing them as list
means that v in list_circle
check takes O(n)
so the whole algorithm is potentially O(n^2)
. I think a bad example for you would be something like "P" but with a very small loop and with a very long line and you start at the bottom of that line so you have to travel the whole line until you finally find the cycle.
I suspect that if you merge them into a single dict()
to store your DFS results as
visited[child] = parent
it will be harder to create a bad case for it and you still can easily re-construct a cycle from that information by travelling back along the two ways from the first double-visited point.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.