Skip to content

Commit b530dc4

Browse files
authored
Added Kosaraju's algorithm for SCC (#233)
1 parent be07fdf commit b530dc4

File tree

3 files changed

+137
-3
lines changed

3 files changed

+137
-3
lines changed

pydatastructs/graphs/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
breadth_first_search,
1212
breadth_first_search_parallel,
1313
minimum_spanning_tree,
14-
minimum_spanning_tree_parallel
14+
minimum_spanning_tree_parallel,
15+
strongly_connected_components
1516
)
1617

1718
__all__.extend(algorithms.__all__)

pydatastructs/graphs/algorithms.py

Lines changed: 104 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
'breadth_first_search',
1616
'breadth_first_search_parallel',
1717
'minimum_spanning_tree',
18-
'minimum_spanning_tree_parallel'
18+
'minimum_spanning_tree_parallel',
19+
'strongly_connected_components'
1920
]
2021

2122
def breadth_first_search(
@@ -445,3 +446,105 @@ def minimum_spanning_tree_parallel(graph, algorithm, num_threads):
445446
"isn't implemented for finding minimum spanning trees."
446447
%(algorithm, graph._impl))
447448
return getattr(algorithms, func)(graph, num_threads)
449+
450+
def _visit(graph, vertex, visited, incoming, L):
451+
stack = [vertex]
452+
while stack:
453+
top = stack[-1]
454+
if not visited.get(top, False):
455+
visited[top] = True
456+
for node in graph.neighbors(top):
457+
if incoming.get(node.name, None) is None:
458+
incoming[node.name] = []
459+
incoming[node.name].append(top)
460+
if not visited.get(node.name, False):
461+
stack.append(node.name)
462+
if top is stack[-1]:
463+
L.append(stack.pop())
464+
465+
def _assign(graph, u, incoming, assigned, component):
466+
stack = [u]
467+
while stack:
468+
top = stack[-1]
469+
if not assigned.get(top, False):
470+
assigned[top] = True
471+
component.add(top)
472+
for u in incoming[top]:
473+
if not assigned.get(u, False):
474+
stack.append(u)
475+
if top is stack[-1]:
476+
stack.pop()
477+
478+
def _strongly_connected_components_kosaraju_adjacency_list(graph):
479+
visited, incoming, L = dict(), dict(), []
480+
for u in graph.vertices:
481+
if not visited.get(u, False):
482+
_visit(graph, u, visited, incoming, L)
483+
484+
assigned = dict()
485+
components = []
486+
for i in range(-1, -len(L) - 1, -1):
487+
comp = set()
488+
if not assigned.get(L[i], False):
489+
_assign(graph, L[i], incoming, assigned, comp)
490+
if comp:
491+
components.append(comp)
492+
493+
return components
494+
495+
_strongly_connected_components_kosaraju_adjacency_matrix = \
496+
_strongly_connected_components_kosaraju_adjacency_list
497+
498+
def strongly_connected_components(graph, algorithm):
499+
"""
500+
Computes strongly connected components for the given
501+
graph and algorithm.
502+
503+
Parameters
504+
==========
505+
506+
graph: Graph
507+
The graph whose minimum spanning tree
508+
has to be computed.
509+
algorithm: str
510+
The algorithm which should be used for
511+
computing strongly connected components.
512+
Currently the following algorithms are
513+
supported,
514+
'kosaraju' -> Kosaraju's algorithm as given in
515+
[1].
516+
517+
Returns
518+
=======
519+
520+
components: list
521+
Python list with each element as set of vertices.
522+
523+
Examples
524+
========
525+
526+
>>> from pydatastructs import Graph, AdjacencyListGraphNode
527+
>>> from pydatastructs import strongly_connected_components
528+
>>> v1, v2, v3 = [AdjacencyListGraphNode(i) for i in range(3)]
529+
>>> g = Graph(v1, v2, v3)
530+
>>> g.add_edge(v1.name, v2.name)
531+
>>> g.add_edge(v2.name, v3.name)
532+
>>> g.add_edge(v3.name, v1.name)
533+
>>> scc = strongly_connected_components(g, 'kosaraju')
534+
>>> scc == [{'2', '0', '1'}]
535+
True
536+
537+
References
538+
==========
539+
540+
.. [1] https://en.wikipedia.org/wiki/Kosaraju%27s_algorithm
541+
542+
"""
543+
import pydatastructs.graphs.algorithms as algorithms
544+
func = "_strongly_connected_components_" + algorithm + "_" + graph._impl
545+
if not hasattr(algorithms, func):
546+
raise NotImplementedError(
547+
"Currently %s algoithm for %s implementation of graphs "
548+
"isn't implemented for finding strongly connected components."
549+
%(algorithm, graph._impl))
550+
return getattr(algorithms, func)(graph)

pydatastructs/graphs/tests/test_algorithms.py

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

55

66
def test_breadth_first_search():
@@ -155,3 +155,33 @@ def _test_minimum_spanning_tree(func, ds, algorithm, *args):
155155
_test_minimum_spanning_tree(fmstp, "List", "kruskal", 3)
156156
_test_minimum_spanning_tree(fmstp, "Matrix", "kruskal", 3)
157157
_test_minimum_spanning_tree(fmstp, "List", "prim", 3)
158+
159+
def test_strongly_connected_components():
160+
161+
def _test_strongly_connected_components(func, ds, algorithm, *args):
162+
import pydatastructs.utils.misc_util as utils
163+
GraphNode = getattr(utils, "Adjacency" + ds + "GraphNode")
164+
a, b, c, d, e, f, g, h = \
165+
[GraphNode(chr(x)) for x in range(ord('a'), ord('h') + 1)]
166+
graph = Graph(a, b, c, d, e, f, g, h)
167+
graph.add_edge(a.name, b.name)
168+
graph.add_edge(b.name, c.name)
169+
graph.add_edge(b.name, f.name)
170+
graph.add_edge(b.name, e.name)
171+
graph.add_edge(c.name, d.name)
172+
graph.add_edge(c.name, g.name)
173+
graph.add_edge(d.name, h.name)
174+
graph.add_edge(d.name, c.name)
175+
graph.add_edge(e.name, f.name)
176+
graph.add_edge(e.name, a.name)
177+
graph.add_edge(f.name, g.name)
178+
graph.add_edge(g.name, f.name)
179+
graph.add_edge(h.name, d.name)
180+
graph.add_edge(h.name, g.name)
181+
comps = func(graph, algorithm)
182+
expected_comps = [{'e', 'a', 'b'}, {'d', 'c', 'h'}, {'g', 'f'}]
183+
assert comps == expected_comps
184+
185+
scc = strongly_connected_components
186+
_test_strongly_connected_components(scc, "List", "kosaraju")
187+
_test_strongly_connected_components(scc, "Matrix", "kosaraju")

0 commit comments

Comments
 (0)