Skip to content

Commit b5aa7be

Browse files
prak74cclausspre-commit-ci[bot]
authored
Add doctests to DFS and BFS. Contributes to #9943 (#11757)
* Correct implementation and add tests for dfs and bfs * Ruff check * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * updating DIRECTORY.md * Simplify visited list update in DFS --------- Co-authored-by: Christian Clauss <cclauss@me.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <pre-commit-ci[bot]@users.noreply.github.com>
1 parent 25548bd commit b5aa7be

2 files changed

Lines changed: 208 additions & 67 deletions

File tree

‎DIRECTORY.md‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1164,6 +1164,7 @@
11641164
* [Sol2](project_euler/problem_012/sol2.py)
11651165
* Problem 013
11661166
* [Sol1](project_euler/problem_013/sol1.py)
1167+
* [Sol2](project_euler/problem_013/sol2.py)
11671168
* Problem 014
11681169
* [Sol1](project_euler/problem_014/sol1.py)
11691170
* [Sol2](project_euler/problem_014/sol2.py)
@@ -1344,6 +1345,8 @@
13441345
* [Sol1](project_euler/problem_107/sol1.py)
13451346
* Problem 109
13461347
* [Sol1](project_euler/problem_109/sol1.py)
1348+
* Problem 111
1349+
* [Sol1](project_euler/problem_111/sol1.py)
13471350
* Problem 112
13481351
* [Sol1](project_euler/problem_112/sol1.py)
13491352
* Problem 113
@@ -1366,6 +1369,8 @@
13661369
* [Sol1](project_euler/problem_122/sol1.py)
13671370
* Problem 123
13681371
* [Sol1](project_euler/problem_123/sol1.py)
1372+
* Problem 124
1373+
* [Sol1](project_euler/problem_124/sol1.py)
13691374
* Problem 125
13701375
* [Sol1](project_euler/problem_125/sol1.py)
13711376
* Problem 129
@@ -1376,6 +1381,12 @@
13761381
* [Sol1](project_euler/problem_135/sol1.py)
13771382
* Problem 136
13781383
* [Sol1](project_euler/problem_136/sol1.py)
1384+
* Problem 137
1385+
* [Sol1](project_euler/problem_137/sol1.py)
1386+
* Problem 138
1387+
* [Sol1](project_euler/problem_138/sol1.py)
1388+
* Problem 142
1389+
* [Sol1](project_euler/problem_142/sol1.py)
13791390
* Problem 144
13801391
* [Sol1](project_euler/problem_144/sol1.py)
13811392
* Problem 145

‎graphs/directed_and_undirected_weighted_graph.py‎

Lines changed: 197 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,23 @@ class DirectedGraph:
1010
def __init__(self) -> None:
1111
self.graph = {}
1212

13-
# adding vertices and edges
14-
# adding the weight is optional
15-
# handles repetition
1613
def add_pair(self, u, v, w=1) -> None:
14+
"""
15+
Adds a directed edge u->v with weight w.
16+
Add vertices and edges
17+
Add the weight is optional
18+
Handle repetition
19+
20+
>>> dg = DirectedGraph()
21+
>>> dg.add_pair(-1,2)
22+
>>> dg.add_pair(1,3,5)
23+
>>> dg.add_pair(1,3,5)
24+
>>> dg.add_pair(1,3,6)
25+
>>> dg.all_nodes()
26+
[-1, 2, 1, 3]
27+
>>> dg.graph[1]
28+
[[5, 3], [6, 3]]
29+
"""
1730
if self.graph.get(u):
1831
if self.graph[u].count([w, v]) == 0:
1932
self.graph[u].append([w, v])
@@ -23,53 +36,94 @@ def add_pair(self, u, v, w=1) -> None:
2336
self.graph[v] = []
2437

2538
def all_nodes(self):
39+
"""
40+
Returns list of all nodes in the graph.
41+
>>> dg = DirectedGraph()
42+
>>> dg.all_nodes()
43+
[]
44+
>>> dg.add_pair(1,1)
45+
>>> dg.all_nodes()
46+
[1]
47+
>>> dg.add_pair(2,3,3)
48+
>>> dg.all_nodes()
49+
[1, 2, 3]
50+
"""
2651
return list(self.graph)
2752

2853
# handles if the input does not exist
2954
def remove_pair(self, u, v) -> None:
55+
"""
56+
Removes all edges u->v if it exists.
57+
>>> dg = DirectedGraph()
58+
>>> dg.remove_pair(1,2) # silently exits
59+
>>> dg.add_pair(0,5,2)
60+
>>> dg.graph[0]
61+
[[2, 5]]
62+
>>> dg.remove_pair(5,0)
63+
>>> dg.graph[0]
64+
[[2, 5]]
65+
>>> dg.remove_pair(0,5)
66+
>>> dg.graph[0]
67+
[]
68+
"""
3069
if self.graph.get(u):
3170
for _ in self.graph[u]:
3271
if _[1] == v:
3372
self.graph[u].remove(_)
3473

3574
# if no destination is meant the default value is -1
3675
def dfs(self, s=-2, d=-1):
37-
if s == d:
38-
return []
76+
"""
77+
Performs depth first search from s to find d.
78+
Returns the path s->d as a list.
79+
Returns dfs from s if d is not found
80+
>>> dg = DirectedGraph()
81+
>>> dg.dfs()
82+
[]
83+
>>> dg.add_pair(1,1)
84+
>>> dg.dfs(1,1)
85+
[1]
86+
>>> dg = DirectedGraph()
87+
>>> dg.add_pair(0,1)
88+
>>> dg.add_pair(0,2)
89+
>>> dg.add_pair(1,3)
90+
>>> dg.add_pair(1,4)
91+
>>> dg.add_pair(1,5)
92+
>>> dg.add_pair(2,5)
93+
>>> dg.add_pair(5,6)
94+
>>> dg.dfs(0,6)
95+
[0, 2, 5, 6]
96+
>>> dg.dfs(1,6)
97+
[1, 5, 6]
98+
>>> dg.dfs()
99+
[0, 2, 5, 6, 1, 4, 3]
100+
>>> dg.dfs(1,0)
101+
[1, 5, 6, 4, 3]
102+
"""
39103
stack = []
40104
visited = []
41105
if s == -2:
42-
s = next(iter(self.graph))
43-
stack.append(s)
44-
visited.append(s)
45-
ss = s
46-
47-
while True:
48-
# check if there is any non isolated nodes
49-
if len(self.graph[s]) != 0:
50-
ss = s
51-
for node in self.graph[s]:
52-
if visited.count(node[1]) < 1:
53-
if node[1] == d:
54-
visited.append(d)
55-
return visited
56-
else:
57-
stack.append(node[1])
58-
visited.append(node[1])
59-
ss = node[1]
60-
break
61-
62-
# check if all the children are visited
63-
if s == ss:
64-
stack.pop()
65-
if len(stack) != 0:
66-
s = stack[len(stack) - 1]
106+
if self.graph.get(s, None):
107+
pass # -2 is a node
108+
elif len(self.graph) > 0:
109+
s = next(iter(self.graph))
67110
else:
68-
s = ss
111+
return [] # Graph empty
112+
stack.append(s)
69113

70-
# check if se have reached the starting point
71-
if len(stack) == 0:
72-
return visited
114+
# Run dfs
115+
while len(stack) > 0:
116+
s = stack.pop()
117+
visited.append(s)
118+
# If reached d, return
119+
if s == d:
120+
break
121+
122+
# add not visited child nodes to stack
123+
for _, ss in self.graph[s]:
124+
if visited.count(ss) < 1:
125+
stack.append(ss)
126+
return visited
73127

74128
# c is the count of nodes you want and if you leave it or pass -1 to the function
75129
# the count will be random from 10 to 10000
@@ -84,12 +138,42 @@ def fill_graph_randomly(self, c=-1) -> None:
84138
self.add_pair(i, n, 1)
85139

86140
def bfs(self, s=-2):
141+
"""
142+
Performs breadth first search from s
143+
Returns list.
144+
>>> dg = DirectedGraph()
145+
>>> dg.bfs()
146+
[]
147+
>>> dg.add_pair(1,1)
148+
>>> dg.bfs(1)
149+
[1]
150+
>>> dg = DirectedGraph()
151+
>>> dg.add_pair(0,1)
152+
>>> dg.add_pair(0,2)
153+
>>> dg.add_pair(1,3)
154+
>>> dg.add_pair(1,4)
155+
>>> dg.add_pair(1,5)
156+
>>> dg.add_pair(2,5)
157+
>>> dg.add_pair(5,6)
158+
>>> dg.bfs(0)
159+
[0, 1, 2, 3, 4, 5, 6]
160+
>>> dg.bfs(1)
161+
[1, 3, 4, 5, 6]
162+
>>> dg.bfs()
163+
[0, 1, 2, 3, 4, 5, 6]
164+
"""
87165
d = deque()
88166
visited = []
89167
if s == -2:
90-
s = next(iter(self.graph))
168+
if self.graph.get(s, None):
169+
pass # -2 is a node
170+
elif len(self.graph) > 0:
171+
s = next(iter(self.graph))
172+
else:
173+
return [] # Graph empty
91174
d.append(s)
92175
visited.append(s)
176+
# Run bfs
93177
while d:
94178
s = d.popleft()
95179
if len(self.graph[s]) != 0:
@@ -298,42 +382,59 @@ def remove_pair(self, u, v) -> None:
298382

299383
# if no destination is meant the default value is -1
300384
def dfs(self, s=-2, d=-1):
301-
if s == d:
302-
return []
385+
"""
386+
Performs depth first search from s to find d.
387+
Returns the path s->d as a list.
388+
Returns dfs from s if d is not found
389+
>>> ug = Graph()
390+
>>> ug.dfs()
391+
[]
392+
>>> ug.add_pair(1,1)
393+
>>> ug.dfs(1,1)
394+
[1]
395+
>>> ug = Graph()
396+
>>> ug.add_pair(0,1)
397+
>>> ug.add_pair(0,2)
398+
>>> ug.add_pair(1,3)
399+
>>> ug.add_pair(1,4)
400+
>>> ug.add_pair(1,5)
401+
>>> ug.add_pair(2,5)
402+
>>> ug.add_pair(5,6)
403+
>>> ug.dfs(0,6)
404+
[0, 2, 5, 6]
405+
>>> ug.dfs(1,6)
406+
[1, 5, 6]
407+
>>> ug.dfs()
408+
[0, 2, 5, 6, 1, 4, 3]
409+
>>> ug.dfs(1,0)
410+
[1, 5, 6, 2, 0]
411+
"""
303412
stack = []
304413
visited = []
305414
if s == -2:
306-
s = next(iter(self.graph))
307-
stack.append(s)
308-
visited.append(s)
309-
ss = s
310-
311-
while True:
312-
# check if there is any non isolated nodes
313-
if len(self.graph[s]) != 0:
314-
ss = s
315-
for node in self.graph[s]:
316-
if visited.count(node[1]) < 1:
317-
if node[1] == d:
318-
visited.append(d)
319-
return visited
320-
else:
321-
stack.append(node[1])
322-
visited.append(node[1])
323-
ss = node[1]
324-
break
325-
326-
# check if all the children are visited
327-
if s == ss:
328-
stack.pop()
329-
if len(stack) != 0:
330-
s = stack[len(stack) - 1]
415+
if self.graph.get(s, None):
416+
pass # -2 is a node
417+
elif len(self.graph) > 0:
418+
s = next(iter(self.graph))
331419
else:
332-
s = ss
420+
return [] # Graph empty
421+
stack.append(s)
333422

334-
# check if se have reached the starting point
335-
if len(stack) == 0:
336-
return visited
423+
# Run dfs
424+
while len(stack) > 0:
425+
s = stack.pop()
426+
if visited.count(s) == 1:
427+
continue
428+
visited.append(s)
429+
# If reached d, return
430+
if s == d:
431+
break
432+
433+
# add not visited child nodes to stack
434+
for _, ss in self.graph[s]:
435+
if visited.count(ss) < 1:
436+
stack.append(ss)
437+
return visited
337438

338439
# c is the count of nodes you want and if you leave it or pass -1 to the function
339440
# the count will be random from 10 to 10000
@@ -348,10 +449,39 @@ def fill_graph_randomly(self, c=-1) -> None:
348449
self.add_pair(i, n, 1)
349450

350451
def bfs(self, s=-2):
452+
"""
453+
Performs breadth first search from s
454+
Returns list.
455+
>>> ug = Graph()
456+
>>> ug.bfs()
457+
[]
458+
>>> ug.add_pair(1,1)
459+
>>> ug.bfs(1)
460+
[1]
461+
>>> ug = Graph()
462+
>>> ug.add_pair(0,1)
463+
>>> ug.add_pair(0,2)
464+
>>> ug.add_pair(1,3)
465+
>>> ug.add_pair(1,4)
466+
>>> ug.add_pair(1,5)
467+
>>> ug.add_pair(2,5)
468+
>>> ug.add_pair(5,6)
469+
>>> ug.bfs(0)
470+
[0, 1, 2, 3, 4, 5, 6]
471+
>>> ug.bfs(1)
472+
[1, 0, 3, 4, 5, 2, 6]
473+
>>> ug.bfs()
474+
[0, 1, 2, 3, 4, 5, 6]
475+
"""
351476
d = deque()
352477
visited = []
353478
if s == -2:
354-
s = next(iter(self.graph))
479+
if self.graph.get(s, None):
480+
pass # -2 is a node
481+
elif len(self.graph) > 0:
482+
s = next(iter(self.graph))
483+
else:
484+
return [] # Graph empty
355485
d.append(s)
356486
visited.append(s)
357487
while d:

0 commit comments

Comments
 (0)