A clean, dependency-free Python library for BFS, DFS, and grid traversal algorithms
Project description
๐ Graph & Grid Algorithm Library
A clean, dependency-free Python library for common BFS, DFS, and grid traversal algorithms. Designed as a reliable toolkit for competitive programming, interview prep, and educational use.
๐ฅ Installation
pip install graph-algs
Then import directly from the top-level package โ no need to reference internal subfolders:
from graph_lib import shortest_path, bfs, lee_algorithm, has_cycle_directed
๐ฆ Modules
graph_algorithms/
โโโ bfs/
โ โโโ bfs_general_lib.py # BFS traversal and shortest paths
โ โโโ bfs_undirected_lib.py # Connectivity for undirected graphs
โ โโโ bfs_directed_lib.py # Weak connectivity for directed graphs
โ โโโ grid_bfs_lib.py # Grid BFS โ islands, pathfinding, distance problems
โโโ dfs/
โโโ dfs_general_lib.py # DFS traversal
โโโ dfs_undirected_lib.py # Cycle detection for undirected graphs
โโโ dfs_directed_lib.py # Cycle detection for directed graphs
โ๏ธ Implementation Conventions
Understanding these conventions is essential for using the library correctly.
Error Handling โ Exceptions
All functions raise exceptions on invalid or degenerate input rather than returning a sentinel value:
ValueErrorโ empty graph or grid ({},[]), invalid board size (n <= 0)KeyErrorโstart/endnode not present in the graph, out-of-bounds grid coordinates, start or end cell being a wall
bfs({}, 'A') # โ ValueError: Graph is empty!
bfs(graph, 'Z') # โ KeyError: Vertex Z not in graph!
lee_algorithm(grid, (0,0), (9,9)) # โ KeyError: (0, 0) or (9, 9) cell not in grid!
count_islands([]) # โ ValueError: Grid is empty!
No Path / No Result โ None
When inputs are structurally valid but the goal is unreachable, functions return None:
shortest_path(graph, 'A', 'Z') # โ None (Z unreachable from A)
lee_algorithm(grid, (0,0), (3,3)) # โ None (blocked)
rotting_oranges(grid) # โ None (some fresh oranges can never rot)
Input Validation Scope
The library performs only semantic validation โ it checks whether inputs make sense for the algorithm, not whether they conform to a type contract.
โ Checked:
- Whether the graph or grid is empty
- Whether
start/endnodes exist in the graph - Whether grid coordinates are in bounds
- Whether start/end cells are traversable (value
0) - Whether board size is positive (knight moves)
โ Not checked:
- Whether
graphis actually adict - Whether grid rows are all the same length
- Whether node values are hashable
- Whether
start/endare tuples vs. lists in grid functions
If you pass malformed data (e.g., a list instead of a dict, or a grid with jagged rows), you will get a native Python exception.
๐ API Reference
bfs/bfs_general_lib.py
Basic Traversal
bfs(graph, start, visited=None) โ set
Returns the set of all nodes reachable from start. Accepts an optional visited set to continue a traversal across multiple calls.
bfs_order(graph, start, visited=None) โ list
Returns nodes in BFS visit order. Also accepts an optional shared visited set.
bfs_count(graph, start, visited=None) โ int
Returns the count of nodes reachable from start.
Shortest Paths
shortest_path(graph, start, end) โ list | None
Returns the shortest path as a node list. Returns [start] if start == end. Returns None if no path exists.
shortest_path_len(graph, start, end) โ int | None
Returns the length (number of edges) of the shortest path. Returns 0 if start == end. Returns None if no path exists.
bfs/bfs_undirected_lib.py
is_connected_undirected(graph) โ bool
Returns True if the undirected graph is connected.
count_components_undirected(graph) โ int
Returns the number of connected components.
get_components_order_undirected(graph) โ list[list]
Returns a list of components, each as an ordered BFS traversal list.
largest_component_size_undirected(graph) โ int
Returns the size of the largest component.
shortest_path_len_undirected(graph, start, end) โ int | None
Faster shortest path length using bidirectional BFS. Requires both start and end to be present in the graph. Returns None if no path exists.
bfs/bfs_directed_lib.py
Connectivity functions for directed graphs use weak connectivity โ the graph is normalised to undirected via normalize() before traversal.
normalize(graph) โ dict
Converts a directed graph into an undirected one by adding reverse edges. Used internally; safe to call directly.
is_connected_directed(graph) โ bool
Returns True if the directed graph is weakly connected.
count_components_directed(graph) โ int
Returns the number of weakly connected components.
get_components_order_directed(graph) โ list[list]
Returns a list of weakly connected components, each as an ordered BFS traversal list.
largest_component_size_directed(graph) โ int
Returns the size of the largest weakly connected component.
dfs/dfs_general_lib.py
dfs(graph, start, visited=None) โ set
Recursive DFS. Returns the set of all visited nodes. Accepts an optional shared visited set.
dfs_order(graph, start) โ list
Iterative DFS using an explicit stack. Returns nodes in DFS visit order. Does not accept a shared visited set โ always starts fresh.
dfs/dfs_undirected_lib.py
has_cycle_undirected(graph) โ bool
Detects cycles in an undirected graph using parent tracking.
dfs/dfs_directed_lib.py
has_cycle_directed(graph) โ bool
Detects cycles in a directed graph using the white/gray/black coloring method.
bfs/grid_bfs_lib.py
Grid convention:
0= traversable cell,1= wall/obstacle (exceptdist_to_nearest_oneandrotting_orangeswhich use domain-specific values).
Island Problems
count_islands(grid) โ int
Counts the number of connected regions of 0-cells (4-directional).
get_islands_order(grid) โ list[list[tuple]]
Returns all islands as lists of (row, col) coordinates in BFS order.
largest_island_area(grid) โ int
Returns the cell count of the largest island.
Pathfinding (Lee Algorithm)
lee_algorithm(grid, start, end) โ list[tuple] | None
Finds the shortest path in a grid from start to end. Returns path as a list of (row, col) tuples. Returns None if unreachable.
lee_algorithm_len(grid, start, end) โ int | None
Returns the length of the shortest grid path. Returns 0 if start == end. Returns None if unreachable.
Distance & Simulation
dist_to_nearest_one(grid) โ list[list[int]]
Multi-source BFS. Returns a grid where each cell contains its distance to the nearest 1-cell. Cells that are 1 have distance 0.
rotting_oranges(grid) โ int | None
Simulates rotting spread (LeetCode 994). Grid values: 0 = empty, 1 = fresh orange, 2 = rotten orange. Returns minutes until all oranges rot, or None if impossible.
Knight Moves
knight_moves(n, start, end) โ list[tuple] | None
Finds the shortest path for a chess knight on an nรn board. Returns path as (row, col) tuples. Returns None if unreachable.
knight_moves_len(n, start, end) โ int | None
Returns the minimum number of moves for a knight to travel from start to end. Returns None if unreachable.
๐บ๏ธ Graph Format
All graph functions expect an adjacency list as a Python dict.
Implicit sink nodes in directed graphs
In a directed graph, nodes that have no outgoing edges don't have to appear as explicit keys โ they can exist only as values in other nodes' neighbour lists. This is supported via graph.get(node, []) instead of graph[node] everywhere neighbours are iterated, so a missing key is safely treated as having no outgoing edges.
# โ
Both forms are equivalent and accepted
# Explicit โ sink nodes listed with empty neighbour lists
graph = {
'A': ['B', 'C'],
'B': ['D'],
'C': [], # sink, explicitly listed
'D': [] # sink, explicitly listed
}
# Compact โ sink nodes omitted as keys entirely
graph = {
'A': ['B', 'C'],
'B': ['D'],
# C and D are implied by appearing in neighbour lists
}
โ ๏ธ Exception: functions that validate
startby checkingif start not in graphwill raiseKeyErrorif a sink node is passed asstartand is not an explicit key. If you need to start traversal from a sink, include it explicitly with an empty list.
For undirected graphs, all edges must still be listed in both directions:
graph = {
1: [2, 3],
2: [1, 4],
3: [1],
4: [2]
}
Nodes can be any hashable type (strings, integers, tuples, etc.).
๐บ๏ธ Grid Format
Grid functions expect a 2D list of integers:
grid = [
[0, 0, 1, 0],
[1, 0, 0, 0],
[0, 0, 1, 1],
[0, 0, 0, 0],
]
Coordinates are always (row, col). All movement is 4-directional (up, down, left, right).
๐ก Usage Examples
from graph_lib import (
shortest_path, count_components_undirected, count_components_directed,
dfs_order, has_cycle_directed, has_cycle_undirected,
lee_algorithm, rotting_oranges
)
# Shortest path in an undirected graph
graph = {1: [2, 3], 2: [1, 4], 3: [1], 4: [2]}
print(shortest_path(graph, 1, 4)) # โ [1, 2, 4]
# Count weakly connected components in a directed graph
dgraph = {'A': ['B'], 'B': [], 'C': ['D'], 'D': []}
print(count_components_directed(dgraph)) # โ 2
# Count connected components in an undirected graph
ugraph = {1: [2], 2: [1], 3: [4], 4: [3]}
print(count_components_undirected(ugraph)) # โ 2
# Cycle detection
print(has_cycle_directed({'A': ['B'], 'B': ['A']})) # โ True
print(has_cycle_undirected({1: [2], 2: [1, 3], 3: [2]})) # โ False
# Grid shortest path
grid = [[0, 0, 0], [1, 1, 0], [0, 0, 0]]
print(lee_algorithm(grid, (0, 0), (2, 2))) # โ [(0,0), (0,1), (0,2), (1,2), (2,2)]
# Rotting oranges
grid = [[2, 1, 1], [1, 1, 0], [0, 1, 1]]
print(rotting_oranges(grid)) # โ 4
๐ Quick Reference: Return Values
| Situation | Return Value |
|---|---|
| Invalid / empty input | ValueError or KeyError |
| Valid input, goal unreachable | None |
start == end (path functions) |
[start] or 0 |
| Success | Result value or list |
Made with โค๏ธ and Python
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file graph_algs-0.3.2.tar.gz.
File metadata
- Download URL: graph_algs-0.3.2.tar.gz
- Upload date:
- Size: 10.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
08f92fb449665c6d3b26cb74c46397dd3b0392c784b6f73fa807ed26ea991d96
|
|
| MD5 |
570bcc708a840194bcb02954d92b3fc3
|
|
| BLAKE2b-256 |
d77488c3aeb330fd44b257c9a4a27af2b95e8068ce1ff265735439775d5d7f60
|
File details
Details for the file graph_algs-0.3.2-py3-none-any.whl.
File metadata
- Download URL: graph_algs-0.3.2-py3-none-any.whl
- Upload date:
- Size: 12.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ffa2487d2022fa8ff24a4b47aaf05bf1b4d9841dce9fa2af51329236e328b841
|
|
| MD5 |
fef5faebddd007fb76abd96045b017a5
|
|
| BLAKE2b-256 |
951e560a50d918a9f9d3baf46d7064a1df5c5c451eb114e9ddff2dd19eece0da
|