Skip to content

Commit 6fbdf2d

Browse files
committed
Added Bipartite Algorithm
Signed-off-by: Hargun Kaur <[email protected]>
1 parent f34b7d6 commit 6fbdf2d

File tree

3 files changed

+94
-3
lines changed

3 files changed

+94
-3
lines changed

pydatastructs/graphs/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
all_pair_shortest_paths,
2222
topological_sort,
2323
topological_sort_parallel,
24-
max_flow
24+
max_flow,
25+
is_bipartite
2526
)
2627

2728
__all__.extend(algorithms.__all__)

pydatastructs/graphs/algorithms.py

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@
2323
'all_pair_shortest_paths',
2424
'topological_sort',
2525
'topological_sort_parallel',
26-
'max_flow'
26+
'max_flow',
27+
'is_bipartite'
2728
]
2829

2930
Stack = Queue = deque
@@ -1216,3 +1217,57 @@ def max_flow(graph, source, sink, algorithm='edmonds_karp', **kwargs):
12161217
f"Currently {algorithm} algorithm isn't implemented for "
12171218
"performing max flow on graphs.")
12181219
return getattr(algorithms, func)(graph, source, sink)
1220+
1221+
def is_bipartite(graph):
1222+
"""
1223+
Determines whether the given undirected graph is bipartite using BFS.
1224+
1225+
Parameters
1226+
==========
1227+
graph : Graph
1228+
An undirected graph instance.
1229+
1230+
Returns
1231+
=======
1232+
(bool, dict)
1233+
A tuple where the first element is True if the graph is bipartite and False otherwise.
1234+
The second element is a dictionary mapping each vertex (its name) to a color (0 or 1)
1235+
if the graph is bipartite; if not, the dictionary may be partially filled.
1236+
1237+
Examples
1238+
========
1239+
>>> from pydatastructs import Graph, AdjacencyListGraphNode, is_bipartite
1240+
>>> v0 = AdjacencyListGraphNode(0)
1241+
>>> v1 = AdjacencyListGraphNode(1)
1242+
>>> v2 = AdjacencyListGraphNode(2)
1243+
>>> v3 = AdjacencyListGraphNode(3)
1244+
>>> graph = Graph(v0, v1, v2, v3, implementation='adjacency_list')
1245+
>>> graph.add_edge(v0.name, v1.name)
1246+
>>> graph.add_edge(v1.name, v2.name)
1247+
>>> graph.add_edge(v2.name, v3.name)
1248+
>>> graph.add_edge(v3.name, v0.name)
1249+
>>> is_bipartite(graph)
1250+
(True, {'0': 0, '1': 1, '2': 0, '3': 1})
1251+
1252+
References
1253+
==========
1254+
.. [1] https://en.wikipedia.org/wiki/Bipartite_graph
1255+
"""
1256+
from collections import deque
1257+
color = {}
1258+
1259+
for vertex in graph.vertices:
1260+
vertex_name = vertex.name if hasattr(vertex, "name") else vertex
1261+
if vertex_name not in color:
1262+
color[vertex_name] = 0
1263+
queue = deque([vertex_name])
1264+
while queue:
1265+
u = queue.popleft()
1266+
for neighbor in graph.neighbors(u):
1267+
v = neighbor.name if hasattr(neighbor, "name") else neighbor
1268+
if v not in color:
1269+
color[v] = 1 - color[u]
1270+
queue.append(v)
1271+
elif color[v] == color[u]:
1272+
return (False, color)
1273+
return (True, color)

pydatastructs/graphs/tests/test_algorithms.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
breadth_first_search_parallel, minimum_spanning_tree,
33
minimum_spanning_tree_parallel, strongly_connected_components,
44
depth_first_search, shortest_paths,all_pair_shortest_paths, topological_sort,
5-
topological_sort_parallel, max_flow)
5+
topological_sort_parallel, max_flow, is_bipartite)
66
from pydatastructs.utils.raises_util import raises
77

88
def test_breadth_first_search():
@@ -448,3 +448,38 @@ def _test_max_flow(ds, algorithm):
448448
_test_max_flow("Matrix", "edmonds_karp")
449449
_test_max_flow("List", "dinic")
450450
_test_max_flow("Matrix", "dinic")
451+
452+
def test_is_bipartite():
453+
import pydatastructs.utils.misc_util as utils
454+
def _test_bipartite(ds):
455+
GraphNode = getattr(utils, "Adjacency" + ds + "GraphNode")
456+
impl = 'adjacency_list' if ds == "List" else 'adjacency_matrix'
457+
458+
v0 = GraphNode(0)
459+
v1 = GraphNode(1)
460+
v2 = GraphNode(2)
461+
v3 = GraphNode(3)
462+
graph = Graph(v0, v1, v2, v3, implementation=impl)
463+
graph.add_edge(v0.name, v1.name)
464+
graph.add_edge(v1.name, v2.name)
465+
graph.add_edge(v2.name, v3.name)
466+
graph.add_edge(v3.name, v0.name)
467+
bip, colors = is_bipartite(graph)
468+
assert bip is True
469+
assert colors[v0.name] != colors[v1.name]
470+
assert colors[v1.name] != colors[v2.name]
471+
assert colors[v2.name] != colors[v3.name]
472+
assert colors[v3.name] != colors[v0.name]
473+
474+
u0 = GraphNode(0)
475+
u1 = GraphNode(1)
476+
u2 = GraphNode(2)
477+
graph = Graph(u0, u1, u2, implementation=impl)
478+
graph.add_edge(u0.name, u1.name)
479+
graph.add_edge(u1.name, u2.name)
480+
graph.add_edge(u2.name, u0.name)
481+
bip, _ = is_bipartite(graph)
482+
assert bip is False
483+
484+
_test_bipartite("List")
485+
_test_bipartite("Matrix")

0 commit comments

Comments
 (0)