-
Notifications
You must be signed in to change notification settings - Fork 0
/
dfs.pyx
223 lines (175 loc) · 5.07 KB
/
dfs.pyx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
from time import time
from stack cimport stack_c, create_stack, push, pop, peek, \
free_stack, size_s, print_stack, is_empty_s
from array_c cimport array_c, push_back_arr, create_arr, free_arr
from readg cimport read_graph, read_graphs
from graph cimport graph_c, node_c, free_graph, dict2graph, rand_dict_graph
from c_utils cimport frand
from libc.stdlib cimport rand
from utils import print_func_name
""" ################### Depth-First Search using Recursion ############# """
cdef void dfs_rec(graph_c* g, size_t s, array_c* out=NULL):
"""
Recursive DFS starting from s vertex
:param g: C graph
:param s: starting vertex
:param out: ordered array of visited vertices
"""
cdef:
size_t i, j
node_c* nd
nd = g.node[s]
nd.explored = True
#action
if out:
push_back_arr(out, s)
if nd.adj:
for i in range(nd.adj.size):
j = nd.adj.items[i]
if not g.node[j].explored:
dfs_rec(g, j, out)
return
""" ######### Depth-First Search using Stack data-structure ########### """
cdef void dfs_stack(graph_c* g, size_t s, array_c* out=NULL):
"""
DFS using stack. The difference from classical realization is that during exploration
we use peek() and vertices stay in the stack. We remove from stack when there is no
adjacent nodes. Direct analogy to recusrsive procedure and gives us correct finishing
time values for topological sorting and strongly connected components (SCC).
:param g: inpur C graph
:param s: starting vertex
:param out: (optional) array of visited vertices
"""
cdef:
size_t i, j, v
node_c* nd
stack_c * stack = create_stack(g.len * 2)
push(stack, s)
while not is_empty_s(stack):
v = peek(stack)
nd = g.node[v]
nd.leader = s
# pop vertex if already explored
if nd.explored:
pop(stack)
continue
else:
nd.explored = True
# action
if out:
push_back_arr(out, v)
# push each edge of v
if nd.adj:
for i in range(nd.adj.size):
j = nd.adj.items[i]
if not g.node[j].explored:
push(stack, j)
free_stack(stack)
cdef void dfs_ordered_loop(graph_c* g, array_c* order):
cdef size_t i, j
for i in range(g.len):
j = order.items[i]
if not g.node[j].explored:
dfs_stack(g, j)
""" ################################################################ """
""" ######################### UNIT TESTS ########################### """
""" ################################################################ """
def test_dfs_1():
graph = {0: [0, 1, 2],
1: [3],
2: [],
3: [4],
4: []}
cdef graph_c* g = dict2graph(graph)
# print_graph(g)
dfs_rec(g, 0)
free_graph(g)
def test_dfs_2():
graph = {0: [0, 1, 2],
1: [3],
2: [],
3: [4],
4: []}
cdef graph_c* g = dict2graph(graph)
# print_graph(g)
dfs_stack(g, 0)
free_graph(g)
def test_dfs_3():
graph = {0: [2, 1, 4],
1: [],
2: [4, 3],
3: [],
4: []}
cdef graph_c* g = dict2graph(graph)
cdef array_c* out = create_arr(g.len)
dfs_rec(g, 0, out)
assert out.items[0] == 0
assert out.items[1] == 2
assert out.items[2] == 4
assert out.items[3] == 3
assert out.items[4] == 1
# print_stack(out)
free_graph(g)
free_arr(out)
def test_dfs_4():
graph = {0: [1, 2],
1: [],
2: [3, 4],
3: [],
4: []}
cdef graph_c* g = dict2graph(graph)
# print_graph(g)
cdef array_c* out = create_arr(g.len)
dfs_stack(g, 0, out)
# print("dfs len:", size_s(out))
assert out.items[0] == 0
assert out.items[1] == 2
assert out.items[2] == 4
assert out.items[3] == 3
assert out.items[4] == 1
# print_stack(out)
free_graph(g)
free_arr(out)
def test_dfs_random():
DEF SIZE = 30
cdef:
graph_c* g
node_c * nd
size_t i, j, k
array_c * out = create_arr(SIZE)
for i in range(1000):
graph = rand_dict_graph(SIZE, rand() % (SIZE), selfloops=True)
g = dict2graph(graph)
dfs_stack(g, 0, out)
assert out.size <= g.len
# no duplicates
for j in range(out.size - 1):
for k in range(j + 1, out.size):
assert out.items[j] != out.items[k]
out.size = 0
free_graph(g)
free_arr(out)
# def test_dfs_big():
# cdef:
# graph_c* g
# graph_c* g_rev
#
# g = read_graph("scc.txt")
# dfs_stack(g, 0)
#
#
# def test_dfs_loop_big():
# cdef:
# size_t i
# graph_c* g
# graph_c* g_rev
#
# g = read_graph("scc.txt")
#
# cdef:
# array_c * order = create_arr(g.len)
# for i in range(g.len):
# order.items[i] = i
# order.size = g.len
#
# dfs_ordered_loop(g, order)