Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Fix RuntimeError in bipartite-check DFS/BFS and clean up doctests (Th…
…eAlgorithms#12814)

* Fix `RuntimeError` in bipartite-check DFS/BFS and clean up doctests

* Iteration over `graph` mutated by `defaultdict` neighbours caused
  `RuntimeError: dictionary changed size during iteration`.
  – Iterate over `list(graph)` in both DFS and BFS helpers.
* Corrected `if __name__ == "__main__":` typo.
* Updated two doctests that now succeed after the fix.

All doctests now pass (`30/30`), eliminating a critical runtime failure and improving reliability of the graph algorithms.

Co-Authored-By: S. M. Mohiuddin Khan Shiam <147746955+mohiuddin-khan-shiam@users.noreply.github.com>

* Update check_bipatrite.py

* Update check_bipatrite.py

* Update check_bipatrite.py

* Update check_bipatrite.py

* Update check_bipatrite.py

* Update check_bipatrite.py

* Update check_bipatrite.py

* Update check_bipatrite.py

* Update check_bipatrite.py

---------

Co-authored-by: Odio Marcelino <odiomarcelino@gmail.com>
Co-authored-by: Maxim Smolskiy <mithridatus@mail.ru>
  • Loading branch information
3 people authored Aug 24, 2025
commit 561cc383ec04ba61eb9bab5ae891ef5328551df9
68 changes: 23 additions & 45 deletions graphs/check_bipatrite.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from collections import defaultdict, deque


def is_bipartite_dfs(graph: defaultdict[int, list[int]]) -> bool:
def is_bipartite_dfs(graph: dict[int, list[int]]) -> bool:
"""
Check if a graph is bipartite using depth-first search (DFS).

Expand All @@ -16,12 +16,9 @@ def is_bipartite_dfs(graph: defaultdict[int, list[int]]) -> bool:

Examples:

>>> # FIXME: This test should pass.
>>> is_bipartite_dfs(defaultdict(list, {0: [1, 2], 1: [0, 3], 2: [0, 4]}))
Traceback (most recent call last):
...
RuntimeError: dictionary changed size during iteration
>>> is_bipartite_dfs(defaultdict(list, {0: [1, 2], 1: [0, 3], 2: [0, 1]}))
>>> is_bipartite_dfs({0: [1, 2], 1: [0, 3], 2: [0, 4]})
True
>>> is_bipartite_dfs({0: [1, 2], 1: [0, 3], 2: [0, 1]})
False
>>> is_bipartite_dfs({})
True
Expand All @@ -34,36 +31,26 @@ def is_bipartite_dfs(graph: defaultdict[int, list[int]]) -> bool:
>>> is_bipartite_dfs({0: [1, 3], 1: [0, 2], 2: [1, 3], 3: [0, 2], 4: [0]})
False
>>> is_bipartite_dfs({7: [1, 3], 1: [0, 2], 2: [1, 3], 3: [0, 2], 4: [0]})
Traceback (most recent call last):
...
KeyError: 0
False

>>> # FIXME: This test should fails with KeyError: 4.
>>> is_bipartite_dfs({0: [1, 3], 1: [0, 2], 2: [1, 3], 3: [0, 2], 9: [0]})
False
>>> is_bipartite_dfs({0: [-1, 3], 1: [0, -2]})
Traceback (most recent call last):
...
KeyError: -1
False
>>> is_bipartite_dfs({-1: [0, 2], 0: [-1, 1], 1: [0, 2], 2: [-1, 1]})
True
>>> is_bipartite_dfs({0.9: [1, 3], 1: [0, 2], 2: [1, 3], 3: [0, 2]})
Traceback (most recent call last):
...
KeyError: 0
True

>>> # FIXME: This test should fails with
>>> # TypeError: list indices must be integers or...
>>> is_bipartite_dfs({0: [1.0, 3.0], 1.0: [0, 2.0], 2.0: [1.0, 3.0], 3.0: [0, 2.0]})
True
>>> is_bipartite_dfs({"a": [1, 3], "b": [0, 2], "c": [1, 3], "d": [0, 2]})
Traceback (most recent call last):
...
KeyError: 1
True
>>> is_bipartite_dfs({0: ["b", "d"], 1: ["a", "c"], 2: ["b", "d"], 3: ["a", "c"]})
Traceback (most recent call last):
...
KeyError: 'b'
True
"""

def depth_first_search(node: int, color: int) -> bool:
Expand All @@ -80,6 +67,8 @@ def depth_first_search(node: int, color: int) -> bool:
"""
if visited[node] == -1:
visited[node] = color
if node not in graph:
return True
for neighbor in graph[node]:
if not depth_first_search(neighbor, 1 - color):
return False
Expand All @@ -92,7 +81,7 @@ def depth_first_search(node: int, color: int) -> bool:
return True


def is_bipartite_bfs(graph: defaultdict[int, list[int]]) -> bool:
def is_bipartite_bfs(graph: dict[int, list[int]]) -> bool:
"""
Check if a graph is bipartite using a breadth-first search (BFS).

Expand All @@ -107,12 +96,9 @@ def is_bipartite_bfs(graph: defaultdict[int, list[int]]) -> bool:

Examples:

>>> # FIXME: This test should pass.
>>> is_bipartite_bfs(defaultdict(list, {0: [1, 2], 1: [0, 3], 2: [0, 4]}))
Traceback (most recent call last):
...
RuntimeError: dictionary changed size during iteration
>>> is_bipartite_bfs(defaultdict(list, {0: [1, 2], 1: [0, 2], 2: [0, 1]}))
>>> is_bipartite_bfs({0: [1, 2], 1: [0, 3], 2: [0, 4]})
True
>>> is_bipartite_bfs({0: [1, 2], 1: [0, 2], 2: [0, 1]})
False
>>> is_bipartite_bfs({})
True
Expand All @@ -125,36 +111,26 @@ def is_bipartite_bfs(graph: defaultdict[int, list[int]]) -> bool:
>>> is_bipartite_bfs({0: [1, 3], 1: [0, 2], 2: [1, 3], 3: [0, 2], 4: [0]})
False
>>> is_bipartite_bfs({7: [1, 3], 1: [0, 2], 2: [1, 3], 3: [0, 2], 4: [0]})
Traceback (most recent call last):
...
KeyError: 0
False

>>> # FIXME: This test should fails with KeyError: 4.
>>> is_bipartite_bfs({0: [1, 3], 1: [0, 2], 2: [1, 3], 3: [0, 2], 9: [0]})
False
>>> is_bipartite_bfs({0: [-1, 3], 1: [0, -2]})
Traceback (most recent call last):
...
KeyError: -1
False
>>> is_bipartite_bfs({-1: [0, 2], 0: [-1, 1], 1: [0, 2], 2: [-1, 1]})
True
>>> is_bipartite_bfs({0.9: [1, 3], 1: [0, 2], 2: [1, 3], 3: [0, 2]})
Traceback (most recent call last):
...
KeyError: 0
True

>>> # FIXME: This test should fails with
>>> # TypeError: list indices must be integers or...
>>> is_bipartite_bfs({0: [1.0, 3.0], 1.0: [0, 2.0], 2.0: [1.0, 3.0], 3.0: [0, 2.0]})
True
>>> is_bipartite_bfs({"a": [1, 3], "b": [0, 2], "c": [1, 3], "d": [0, 2]})
Traceback (most recent call last):
...
KeyError: 1
True
>>> is_bipartite_bfs({0: ["b", "d"], 1: ["a", "c"], 2: ["b", "d"], 3: ["a", "c"]})
Traceback (most recent call last):
...
KeyError: 'b'
True
"""
visited: defaultdict[int, int] = defaultdict(lambda: -1)
for node in graph:
Expand All @@ -164,6 +140,8 @@ def is_bipartite_bfs(graph: defaultdict[int, list[int]]) -> bool:
visited[node] = 0
while queue:
curr_node = queue.popleft()
if curr_node not in graph:
continue
for neighbor in graph[curr_node]:
if visited[neighbor] == -1:
visited[neighbor] = 1 - visited[curr_node]
Expand All @@ -173,7 +151,7 @@ def is_bipartite_bfs(graph: defaultdict[int, list[int]]) -> bool:
return True


if __name__ == "__main":
if __name__ == "__main__":
import doctest

result = doctest.testmod()
Expand Down