|
15 | 15 | 'breadth_first_search',
|
16 | 16 | 'breadth_first_search_parallel',
|
17 | 17 | 'minimum_spanning_tree',
|
18 |
| - 'minimum_spanning_tree_parallel' |
| 18 | + 'minimum_spanning_tree_parallel', |
| 19 | + 'strongly_connected_components' |
19 | 20 | ]
|
20 | 21 |
|
21 | 22 | def breadth_first_search(
|
@@ -445,3 +446,105 @@ def minimum_spanning_tree_parallel(graph, algorithm, num_threads):
|
445 | 446 | "isn't implemented for finding minimum spanning trees."
|
446 | 447 | %(algorithm, graph._impl))
|
447 | 448 | 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) |
0 commit comments