29
29
'max_flow' ,
30
30
'maximum_matching' ,
31
31
'maximum_matching_parallel' ,
32
- 'bipartite_coloring'
32
+ 'bipartite_coloring' ,
33
+ 'find_bridges'
33
34
]
34
35
35
36
Stack = Queue = deque
@@ -536,6 +537,52 @@ def _strongly_connected_components_kosaraju_adjacency_list(graph):
536
537
_strongly_connected_components_kosaraju_adjacency_matrix = \
537
538
_strongly_connected_components_kosaraju_adjacency_list
538
539
540
+ def _tarjan_dfs (u , graph , index , stack , indices , low_links , on_stacks , components ):
541
+ indices [u ] = index [0 ]
542
+ low_links [u ] = index [0 ]
543
+ index [0 ] += 1
544
+ stack .append (u )
545
+ on_stacks [u ] = True
546
+
547
+ for node in graph .neighbors (u ):
548
+ v = node .name
549
+ if indices [v ] == - 1 :
550
+ _tarjan_dfs (v , graph , index , stack , indices , low_links , on_stacks , components )
551
+ low_links [u ] = min (low_links [u ], low_links [v ])
552
+ elif on_stacks [v ]:
553
+ low_links [u ] = min (low_links [u ], low_links [v ])
554
+
555
+ if low_links [u ] == indices [u ]:
556
+ component = set ()
557
+ while stack :
558
+ w = stack .pop ()
559
+ on_stacks [w ] = False
560
+ component .add (w )
561
+ if w == u :
562
+ break
563
+ components .append (component )
564
+
565
+ def _strongly_connected_components_tarjan_adjacency_list (graph ):
566
+ index = [0 ] # mutable object
567
+ stack = Stack ([])
568
+ indices , low_links , on_stacks = {}, {}, {}
569
+
570
+ for u in graph .vertices :
571
+ indices [u ] = - 1
572
+ low_links [u ] = - 1
573
+ on_stacks [u ] = False
574
+
575
+ components = []
576
+
577
+ for u in graph .vertices :
578
+ if indices [u ] == - 1 :
579
+ _tarjan_dfs (u , graph , index , stack , indices , low_links , on_stacks , components )
580
+
581
+ return components
582
+
583
+ _strongly_connected_components_tarjan_adjacency_matrix = \
584
+ _strongly_connected_components_tarjan_adjacency_list
585
+
539
586
def strongly_connected_components (graph , algorithm , ** kwargs ):
540
587
"""
541
588
Computes strongly connected components for the given
@@ -554,6 +601,7 @@ def strongly_connected_components(graph, algorithm, **kwargs):
554
601
supported,
555
602
556
603
'kosaraju' -> Kosaraju's algorithm as given in [1].
604
+ 'tarjan' -> Tarjan's algorithm as given in [2].
557
605
backend: pydatastructs.Backend
558
606
The backend to be used.
559
607
Optional, by default, the best available
@@ -583,6 +631,7 @@ def strongly_connected_components(graph, algorithm, **kwargs):
583
631
==========
584
632
585
633
.. [1] https://en.wikipedia.org/wiki/Kosaraju%27s_algorithm
634
+ .. [2] https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm
586
635
587
636
"""
588
637
raise_if_backend_is_not_python (
@@ -1223,6 +1272,7 @@ def max_flow(graph, source, sink, algorithm='edmonds_karp', **kwargs):
1223
1272
"performing max flow on graphs." )
1224
1273
return getattr (algorithms , func )(graph , source , sink )
1225
1274
1275
+
1226
1276
def bipartite_coloring (graph : Graph , ** kwargs ) -> Tuple [bool , Dict ]:
1227
1277
"""
1228
1278
Finds a 2-coloring of the given graph if it is bipartite.
@@ -1265,6 +1315,7 @@ def bipartite_coloring(graph: Graph, **kwargs) -> Tuple[bool, Dict]:
1265
1315
>>> bipartite_coloring(graph, make_undirected=True)
1266
1316
(True, {'v_1': 0, 'v_2': 1, 'v_4': 1, 'v_3': 0})
1267
1317
1318
+
1268
1319
References
1269
1320
==========
1270
1321
@@ -1615,3 +1666,101 @@ def maximum_matching_parallel(graph: Graph, algorithm: str, num_threads: int, **
1615
1666
f"Currently { algorithm } algorithm isn't implemented for "
1616
1667
"finding maximum matching in graphs." )
1617
1668
return getattr (algorithms , func )(graph , num_threads )
1669
+
1670
+ def find_bridges (graph ):
1671
+ """
1672
+ Finds all bridges in an undirected graph using Tarjan's Algorithm.
1673
+
1674
+ Parameters
1675
+ ==========
1676
+ graph : Graph
1677
+ An undirected graph instance.
1678
+
1679
+ Returns
1680
+ ==========
1681
+ List[tuple]
1682
+ A list of bridges, where each bridge is represented as a tuple (u, v)
1683
+ with u <= v.
1684
+
1685
+ Example
1686
+ ========
1687
+ >>> from pydatastructs import Graph, AdjacencyListGraphNode, find_bridges
1688
+ >>> v0 = AdjacencyListGraphNode(0)
1689
+ >>> v1 = AdjacencyListGraphNode(1)
1690
+ >>> v2 = AdjacencyListGraphNode(2)
1691
+ >>> v3 = AdjacencyListGraphNode(3)
1692
+ >>> v4 = AdjacencyListGraphNode(4)
1693
+ >>> graph = Graph(v0, v1, v2, v3, v4, implementation='adjacency_list')
1694
+ >>> graph.add_edge(v0.name, v1.name)
1695
+ >>> graph.add_edge(v1.name, v2.name)
1696
+ >>> graph.add_edge(v2.name, v3.name)
1697
+ >>> graph.add_edge(v3.name, v4.name)
1698
+ >>> find_bridges(graph)
1699
+ [('0', '1'), ('1', '2'), ('2', '3'), ('3', '4')]
1700
+ .. [1] https://en.wikipedia.org/wiki/Bridge_(graph_theory)
1701
+ """
1702
+
1703
+ vertices = list (graph .vertices )
1704
+ processed_vertices = []
1705
+ for v in vertices :
1706
+ if hasattr (v , "name" ):
1707
+ processed_vertices .append (v .name )
1708
+ else :
1709
+ processed_vertices .append (v )
1710
+
1711
+ n = len (processed_vertices )
1712
+ adj = {v : [] for v in processed_vertices }
1713
+ for v in processed_vertices :
1714
+ for neighbor in graph .neighbors (v ):
1715
+ if hasattr (neighbor , "name" ):
1716
+ nbr = neighbor .name
1717
+ else :
1718
+ nbr = neighbor
1719
+ adj [v ].append (nbr )
1720
+
1721
+ mapping = {v : idx for idx , v in enumerate (processed_vertices )}
1722
+ inv_mapping = {idx : v for v , idx in mapping .items ()}
1723
+
1724
+ n_adj = [[] for _ in range (n )]
1725
+ for v in processed_vertices :
1726
+ idx_v = mapping [v ]
1727
+ for u in adj [v ]:
1728
+ idx_u = mapping [u ]
1729
+ n_adj [idx_v ].append (idx_u )
1730
+
1731
+ visited = [False ] * n
1732
+ disc = [0 ] * n
1733
+ low = [0 ] * n
1734
+ parent = [- 1 ] * n
1735
+ bridges_idx = []
1736
+ time = 0
1737
+
1738
+ def dfs (u ):
1739
+ nonlocal time
1740
+ visited [u ] = True
1741
+ disc [u ] = low [u ] = time
1742
+ time += 1
1743
+ for v in n_adj [u ]:
1744
+ if not visited [v ]:
1745
+ parent [v ] = u
1746
+ dfs (v )
1747
+ low [u ] = min (low [u ], low [v ])
1748
+ if low [v ] > disc [u ]:
1749
+ bridges_idx .append ((u , v ))
1750
+ elif v != parent [u ]:
1751
+ low [u ] = min (low [u ], disc [v ])
1752
+
1753
+ for i in range (n ):
1754
+ if not visited [i ]:
1755
+ dfs (i )
1756
+
1757
+ bridges = []
1758
+ for u , v in bridges_idx :
1759
+ a = inv_mapping [u ]
1760
+ b = inv_mapping [v ]
1761
+ if a <= b :
1762
+ bridges .append ((a , b ))
1763
+ else :
1764
+ bridges .append ((b , a ))
1765
+ bridges .sort ()
1766
+ return bridges
0 commit comments