Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 4452dc8

Browse files
authoredApr 9, 2020
DFS implemented (#253)
1 parent bbacdca commit 4452dc8

File tree

3 files changed

+167
-4
lines changed

3 files changed

+167
-4
lines changed
 

‎pydatastructs/graphs/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
breadth_first_search_parallel,
1313
minimum_spanning_tree,
1414
minimum_spanning_tree_parallel,
15-
strongly_connected_components
15+
strongly_connected_components,
16+
depth_first_search
1617
)
1718

1819
__all__.extend(algorithms.__all__)

‎pydatastructs/graphs/algorithms.py

Lines changed: 91 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Contains all the algorithms associated with graph
33
data structure.
44
"""
5-
from collections import deque as Queue
5+
from collections import deque
66
from concurrent.futures import ThreadPoolExecutor
77
from pydatastructs.utils import GraphEdge
88
from pydatastructs.utils.misc_util import _comp
@@ -16,9 +16,12 @@
1616
'breadth_first_search_parallel',
1717
'minimum_spanning_tree',
1818
'minimum_spanning_tree_parallel',
19-
'strongly_connected_components'
19+
'strongly_connected_components',
20+
'depth_first_search'
2021
]
2122

23+
Stack = Queue = deque
24+
2225
def breadth_first_search(
2326
graph, source_node, operation, *args, **kwargs):
2427
"""
@@ -548,3 +551,89 @@ def strongly_connected_components(graph, algorithm):
548551
"isn't implemented for finding strongly connected components."
549552
%(algorithm, graph._impl))
550553
return getattr(algorithms, func)(graph)
554+
555+
def depth_first_search(
556+
graph, source_node, operation, *args, **kwargs):
557+
"""
558+
Implementation of depth first search (DFS)
559+
algorithm.
560+
561+
Parameters
562+
==========
563+
564+
graph: Graph
565+
The graph on which DFS is to be performed.
566+
source_node: str
567+
The name of the source node from where the DFS is
568+
to be initiated.
569+
operation: function
570+
The function which is to be applied
571+
on every node when it is visited.
572+
The prototype which is to be followed is,
573+
`function_name(curr_node, next_node,
574+
arg_1, arg_2, . . ., arg_n)`.
575+
Here, the first two arguments denote, the
576+
current node and the node next to current node.
577+
The rest of the arguments are optional and you can
578+
provide your own stuff there.
579+
580+
Note
581+
====
582+
583+
You should pass all the arguments which you are going
584+
to use in the prototype of your `operation` after
585+
passing the operation function.
586+
587+
Examples
588+
========
589+
590+
>>> from pydatastructs import Graph, AdjacencyListGraphNode
591+
>>> V1 = AdjacencyListGraphNode("V1")
592+
>>> V2 = AdjacencyListGraphNode("V2")
593+
>>> V3 = AdjacencyListGraphNode("V3")
594+
>>> G = Graph(V1, V2, V3)
595+
>>> from pydatastructs import depth_first_search
596+
>>> def f(curr_node, next_node, dest_node):
597+
... return curr_node != dest_node
598+
...
599+
>>> G.add_edge(V1.name, V2.name)
600+
>>> G.add_edge(V2.name, V3.name)
601+
>>> depth_first_search(G, V1.name, f, V3.name)
602+
603+
References
604+
==========
605+
606+
.. [1] https://en.wikipedia.org/wiki/Depth-first_search
607+
"""
608+
import pydatastructs.graphs.algorithms as algorithms
609+
func = "_depth_first_search_" + graph._impl
610+
if not hasattr(algorithms, func):
611+
raise NotImplementedError(
612+
"Currently depth first search isn't implemented for "
613+
"%s graphs."%(graph._impl))
614+
return getattr(algorithms, func)(
615+
graph, source_node, operation, *args, **kwargs)
616+
617+
def _depth_first_search_adjacency_list(
618+
graph, source_node, operation, *args, **kwargs):
619+
dfs_stack = Stack()
620+
visited = dict()
621+
dfs_stack.append(source_node)
622+
visited[source_node] = True
623+
while len(dfs_stack) != 0:
624+
curr_node = dfs_stack.pop()
625+
next_nodes = graph.neighbors(curr_node)
626+
if len(next_nodes) != 0:
627+
for next_node in next_nodes:
628+
if next_node.name not in visited:
629+
status = operation(curr_node, next_node.name, *args, **kwargs)
630+
if not status:
631+
return None
632+
dfs_stack.append(next_node.name)
633+
visited[next_node.name] = True
634+
else:
635+
status = operation(curr_node, "", *args, **kwargs)
636+
if not status:
637+
return None
638+
639+
_depth_first_search_adjacency_matrix = _depth_first_search_adjacency_list

‎pydatastructs/graphs/tests/test_algorithms.py

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from pydatastructs import (breadth_first_search, Graph,
22
breadth_first_search_parallel, minimum_spanning_tree,
3-
minimum_spanning_tree_parallel, strongly_connected_components)
3+
minimum_spanning_tree_parallel, strongly_connected_components,
4+
depth_first_search)
45

56

67
def test_breadth_first_search():
@@ -185,3 +186,75 @@ def _test_strongly_connected_components(func, ds, algorithm, *args):
185186
scc = strongly_connected_components
186187
_test_strongly_connected_components(scc, "List", "kosaraju")
187188
_test_strongly_connected_components(scc, "Matrix", "kosaraju")
189+
190+
def test_depth_first_search():
191+
192+
def _test_depth_first_search(ds):
193+
import pydatastructs.utils.misc_util as utils
194+
GraphNode = getattr(utils, "Adjacency" + ds + "GraphNode")
195+
196+
V1 = GraphNode(0)
197+
V2 = GraphNode(1)
198+
V3 = GraphNode(2)
199+
200+
G1 = Graph(V1, V2, V3)
201+
202+
edges = [
203+
(V1.name, V2.name),
204+
(V2.name, V3.name),
205+
(V1.name, V3.name)
206+
]
207+
208+
for edge in edges:
209+
G1.add_edge(*edge)
210+
211+
parent = dict()
212+
def dfs_tree(curr_node, next_node, parent):
213+
if next_node != "":
214+
parent[next_node] = curr_node
215+
return True
216+
217+
depth_first_search(G1, V1.name, dfs_tree, parent)
218+
assert (parent[V3.name] == V1.name and parent[V2.name] == V1.name) or \
219+
(parent[V3.name] == V2.name and parent[V2.name] == V1.name)
220+
221+
V4 = GraphNode(0)
222+
V5 = GraphNode(1)
223+
V6 = GraphNode(2)
224+
V7 = GraphNode(3)
225+
V8 = GraphNode(4)
226+
227+
edges = [
228+
(V4.name, V5.name),
229+
(V5.name, V6.name),
230+
(V6.name, V7.name),
231+
(V6.name, V4.name),
232+
(V7.name, V8.name)
233+
]
234+
235+
G2 = Graph(V4, V5, V6, V7, V8)
236+
237+
for edge in edges:
238+
G2.add_edge(*edge)
239+
240+
path = []
241+
def path_finder(curr_node, next_node, dest_node, parent, path):
242+
if next_node != "":
243+
parent[next_node] = curr_node
244+
if curr_node == dest_node:
245+
node = curr_node
246+
path.append(node)
247+
while node is not None:
248+
if parent.get(node, None) is not None:
249+
path.append(parent[node])
250+
node = parent.get(node, None)
251+
path.reverse()
252+
return False
253+
return True
254+
255+
parent.clear()
256+
depth_first_search(G2, V4.name, path_finder, V7.name, parent, path)
257+
assert path == [V4.name, V5.name, V6.name, V7.name]
258+
259+
_test_depth_first_search("List")
260+
_test_depth_first_search("Matrix")

0 commit comments

Comments
 (0)
Please sign in to comment.