Dijkstra’s algorithm finds shortest path costs from a source when every edge weight is nonnegative.
Python Dijkstra: nonnegative weights and stale heap entries
Operation contract
The route graph uses integer minute costs and integer node IDs. A relaxation may improve a node after an older cost was already placed in the heap; the algorithm skips that stale entry rather than expanding it again. Unreachable nodes retain None in the public result, so zero remains a valid source cost instead of being overloaded as failure.
Failure and ownership boundary
Negative weights, booleans, undeclared nodes and oversized adjacency lists are rejected before traversal. This algorithm does not solve negative-edge paths or represent time-dependent travel rules. The heap can contain several entries for one node, so its storage cannot be described as one slot per node. Python graph BFS: mark a vertex when it enters the queue is the simpler choice for unit-cost edges.
Working program
import heapq
def route_costs(graph, source):
if type(source) is not int or source not in graph or len(graph) > 64:
raise ValueError("source or graph budget")
for node, edges in graph.items():
if type(node) is not int or len(edges) > 64:
raise ValueError("node budget")
for target, cost in edges:
if type(target) is not int or target not in graph or type(cost) is not int or not 0 <= cost <= 1000:
raise ValueError("nonnegative bounded edges required")
distances = dict.fromkeys(graph, None)
distances[source] = 0
pending = [(0, source)]
while pending:
distance, node = heapq.heappop(pending)
if distance != distances[node]:
continue
for target, cost in graph[node]:
candidate = distance + cost
if distances[target] is None or candidate < distances[target]:
distances[target] = candidate
heapq.heappush(pending, (candidate, target))
return distances
print(route_costs({0: [(1, 9), (2, 2)], 1: [], 2: [(1, 3)], 3: []}, 0))
try:
route_costs({0: [(1, -1)], 1: []}, 0)
except ValueError:
print("negative edge rejected")Output
{0: 0, 1: 5, 2: 2, 3: None}
negative edge rejectedCosts and limits
This lazy-duplicate heap implementation uses O(V+E log(E+1)) work and O(V+E) extra storage in the worst case, with bounded integer operands. Graph validation is part of the measured operation contract.
Common Mistakes
- Reject negative weights before using this greedy relaxation.
- Do not expand a stale heap cost or claim the heap contains only V entries.
Connected lessons
Python graph BFS: mark a vertex when it enters the queue, Python topological sort: dependency order and cycle rejection, Python heapq top-k: define ties before ranking frequencies.
Related Python operation checks
Python Bellman-Ford: negative edges and reachable negative cycles.
Trace the next boundary
Python A-star grid search: use an admissible lower bound for shortest paths.
Follow the integrity boundary
Python 0-1 BFS: shortest paths when every edge costs zero or one.
