Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Python Dijkstra: nonnegative weights and stale heap entries

Last updated: 30 Sept 20264 min read
tutorial
IntermediateBy AITrove Editorial

Dijkstra’s algorithm finds shortest path costs from a source when every edge weight is nonnegative.

Download Python source kit

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

python
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

Output
{0: 0, 1: 5, 2: 2, 3: None}
negative edge rejected

Costs 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.

python
dijkstra
Storage details