Skip to content

feat: implement bidirectional Dijkstra - #789

Open
ChrisJr404 wants to merge 1 commit into
evenfurther:mainfrom
ChrisJr404:feat/dijkstra-bidirectional
Open

feat: implement bidirectional Dijkstra#789
ChrisJr404 wants to merge 1 commit into
evenfurther:mainfrom
ChrisJr404:feat/dijkstra-bidirectional

Conversation

@ChrisJr404

Copy link
Copy Markdown

This adds a bidirectional variant of Dijkstra's algorithm, dijkstra_bidirectional, continuing the work started with bfs_bidirectional (#543 asks for BFS, Dijkstra and A*; BFS landed in v4.14.0, this is the Dijkstra half).

What it does

Two searches run at the same time: one forward from start following successors, and one backward from end following predecessors, each advancing in order of increasing cost. The search stops as soon as a node has been settled by both frontiers, at which point the cheapest complete path discovered so far is optimal. On graphs where the frontier grows quickly with distance this settles far fewer nodes than a single-directional search, since two small frontiers are usually cheaper to grow than one large one.

The signature mirrors the rest of the dijkstra family (single &N start, FnMut(&N) -> IntoIterator<Item = (N, C)>), with an extra predecessors closure so directed graphs are supported. For undirected graphs the same closure can be passed for both, exactly like bfs_bidirectional. Internally it reuses the module's existing SmallestHolder heap entry and the FxIndexMap parent-pointer scheme, so path reconstruction and the cost representation stay consistent with dijkstra.

Notes

  • The cost a predecessors call reports for an edge must match the cost successors reports for that same edge; this is documented on the function. For an undirected graph that falls out naturally.
  • Meeting-path costs are tracked from tentative labels during relaxation (the standard approach), so improvements on either side keep the best path up to date before the frontiers meet.

Tests

  • dijkstra_bidirectional_path_ok / dijkstra_bidirectional_no_path on the existing maze fixture, mirroring the bfs_bidirectional tests, cross-checking the cost against unidirectional dijkstra.
  • dijkstra_bidirectional_ok on the weighted directed graph fixture (with a reversed predecessors function), checking reachability, optimal cost, and that the returned path is a genuine walk whose edges sum to the reported cost.
  • I also ran a local randomized cross-check (20k random directed weighted multigraphs, including parallel and zero-cost edges) against unidirectional dijkstra; costs and reconstructed paths matched in every case. That harness isn't included in the PR.

An examples/dijkstra_bidirectional.rs compares it against plain dijkstra on a large weighted grid (centre to corner), in the same spirit as the existing bfs_bidirectional example.

cargo fmt --all --check, cargo clippy --all-targets and the full test suite (incl. doc tests) are clean.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@ChrisJr404