3535 BFSVertexIteratorState
3636
3737`BFSVertexIteratorState` is a struct to hold the current state of iteration
38- in BFS which is needed for the `Base.iterate()` function. A queue is used to
39- keep track of the vertices which will be visited during BFS. Since the queue
40- can contains repetitions of already visited nodes, we also keep track of that
41- in a `BitVector` so that to skip those nodes.
38+ in BFS which is needed for the `Base.iterate()` function. We use two vectors,
39+ one for the current level nodes and one from the next level nodes to visit
40+ the graph. Since new levels can contains repetitions of already visited nodes,
41+ we also keep track of that in a `BitVector` so as to skip those nodes.
4242"""
4343mutable struct BFSVertexIteratorState
4444 visited:: BitVector
45- queue:: Vector{Int}
46- neighbor_idx:: Int
45+ curr_level:: Vector{Int}
46+ next_level:: Vector{Int}
47+ node_idx:: Int
4748 n_visited:: Int
4849end
4950
@@ -58,14 +59,17 @@ First iteration to visit vertices in a graph using breadth-first search.
5859function Base. iterate (t:: BFSIterator{<:Integer} )
5960 visited = falses (nv (t. graph))
6061 visited[t. source] = true
61- return (t. source, BFSVertexIteratorState (visited, [t. source], 1 , 1 ))
62+ state = BFSVertexIteratorState (visited, [t. source], Int[], 0 , 0 )
63+ return Base. iterate (t, state)
6264end
6365
6466function Base. iterate (t:: BFSIterator{<:AbstractArray} )
6567 visited = falses (nv (t. graph))
66- visited[first (t. source)] = true
67- state = BFSVertexIteratorState (visited, copy (t. source), 1 , 1 )
68- return (first (t. source), state)
68+ curr_level = unique (s for s in t. source)
69+ sort! (curr_level)
70+ visited[curr_level] .= true
71+ state = BFSVertexIteratorState (visited, curr_level, Int[], 0 , 0 )
72+ return Base. iterate (t, state)
6973end
7074
7175"""
7478Iterator to visit vertices in a graph using breadth-first search.
7579"""
7680function Base. iterate (t:: BFSIterator , state:: BFSVertexIteratorState )
77- graph, visited, queue = t. graph, state. visited, state. queue
78- while ! isempty (queue)
79- if state. n_visited == nv (graph)
80- return nothing
81- end
82- # we visit the first node in the queue
83- node_start = first (queue)
84- if ! visited[node_start]
85- visited[node_start] = true
86- state. n_visited += 1
87- return (node_start, state)
88- end
89- # which means we arrive here when the first node was visited.
90- neigh = outneighbors (graph, node_start)
91- if state. neighbor_idx <= length (neigh)
92- node = neigh[state. neighbor_idx]
93- # we update the idx of the neighbor we will visit,
94- # if it is already visited, we repeat
95- state. neighbor_idx += 1
96- if ! visited[node]
97- push! (queue, node)
98- state. visited[node] = true
99- state. n_visited += 1
100- return (node, state)
81+ # we fill nodes in this level
82+ if state. node_idx == length (state. curr_level)
83+ state. n_visited += length (state. curr_level)
84+ state. n_visited == nv (t. graph) && return nothing
85+ @inbounds for node in state. curr_level
86+ for adj_node in outneighbors (t. graph, node)
87+ if ! state. visited[adj_node]
88+ push! (state. next_level, adj_node)
89+ state. visited[adj_node] = true
90+ end
10191 end
102- else
103- # when the first node and its neighbors are visited
104- # we remove the first node of the queue
105- popfirst! (queue)
106- state. neighbor_idx = 1
10792 end
93+ length (state. next_level) == 0 && return nothing
94+ state. curr_level, state. next_level = state. next_level, empty! (state. curr_level)
95+ sort! (state. curr_level)
96+ state. node_idx = 0
10897 end
98+ # we visit all nodes in this level
99+ state. node_idx += 1
100+ @inbounds node = state. curr_level[state. node_idx]
101+ return (node, state)
109102end
0 commit comments