Skip to content

Commit 745add6

Browse files
authored
fix quadratic time in is_cyclic and topological_sort (fix #263) (#266)
* fix quadratic time in is_cyclic and topological_sort * Add comments * renaming queue to stack
1 parent d29e1f2 commit 745add6

File tree

1 file changed

+38
-34
lines changed

1 file changed

+38
-34
lines changed

src/traversals/dfs.jl

Lines changed: 38 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -32,28 +32,30 @@ function is_cyclic end
3232
end
3333
# see https://github.com/mauro3/SimpleTraits.jl/issues/47#issuecomment-327880153 for syntax
3434
@traitfn function is_cyclic(g::AG::IsDirected) where {T,AG<:AbstractGraph{T}}
35+
# 0 if not visited, 1 if visited, 2 if in the current dfs path, 3 if fully explored
3536
vcolor = zeros(UInt8, nv(g))
37+
vertex_stack = Vector{T}()
3638
for v in vertices(g)
3739
vcolor[v] != 0 && continue
38-
S = Vector{T}([v])
40+
push!(vertex_stack, v)
3941
vcolor[v] = 1
40-
while !isempty(S)
41-
u = S[end]
42-
w = 0
43-
for n in outneighbors(g, u)
44-
if vcolor[n] == 1
45-
return true
46-
elseif vcolor[n] == 0
47-
w = n
48-
break
42+
while !isempty(vertex_stack)
43+
u = vertex_stack[end]
44+
if vcolor[u] == 1
45+
vcolor[u] = 2
46+
for n in outneighbors(g, u)
47+
# we hit a loop when reaching back a vertex of the main path
48+
if vcolor[n] == 2
49+
return true
50+
elseif vcolor[n] == 0
51+
# we store neighbors, but these are not yet on the path
52+
vcolor[n] = 1
53+
push!(vertex_stack, n)
54+
end
4955
end
50-
end
51-
if w != 0
52-
vcolor[w] = 1
53-
push!(S, w)
5456
else
55-
vcolor[u] = 2
56-
pop!(S)
57+
vcolor[u] = 3
58+
pop!(vertex_stack)
5759
end
5860
end
5961
end
@@ -85,34 +87,36 @@ graph `g` as a vector of vertices in topological order.
8587
function topological_sort_by_dfs end
8688
# see https://github.com/mauro3/SimpleTraits.jl/issues/47#issuecomment-327880153 for syntax
8789
@traitfn function topological_sort_by_dfs(g::AG::IsDirected) where {T,AG<:AbstractGraph{T}}
90+
# 0 if not visited, 1 if visited, 2 if in the current dfs path, 3 if fully explored
8891
vcolor = zeros(UInt8, nv(g))
8992
verts = Vector{T}()
93+
vertex_stack = Vector{T}()
9094
for v in vertices(g)
9195
vcolor[v] != 0 && continue
92-
S = Vector{T}([v])
96+
push!(vertex_stack, v)
9397
vcolor[v] = 1
94-
while !isempty(S)
95-
u = S[end]
96-
w = 0
97-
for n in outneighbors(g, u)
98-
if vcolor[n] == 1
99-
error("The input graph contains at least one loop.") # TODO 0.7 should we use a different error?
100-
elseif vcolor[n] == 0
101-
w = n
102-
break
98+
while !isempty(vertex_stack)
99+
u = vertex_stack[end]
100+
if vcolor[u] == 1
101+
vcolor[u] = 2
102+
for n in outneighbors(g, u)
103+
# we hit a loop when reaching back a vertex of the main path
104+
if vcolor[n] == 2
105+
error("The input graph contains at least one loop.") # TODO 0.7 should we use a different error?
106+
elseif vcolor[n] == 0
107+
# we store neighbors, but these are not yet on the path
108+
vcolor[n] = 1
109+
push!(vertex_stack, n)
110+
end
103111
end
104-
end
105-
if w != 0
106-
vcolor[w] = 1
107-
push!(S, w)
108112
else
109-
vcolor[u] = 2
110-
push!(verts, u)
111-
pop!(S)
113+
vcolor[u] = 3
114+
pop!(vertex_stack)
115+
pushfirst!(verts, u)
112116
end
113117
end
114118
end
115-
return reverse(verts)
119+
return verts
116120
end
117121

118122
"""

0 commit comments

Comments
 (0)