From 94500b84d4355e322efafc4f21c9d29ffc676826 Mon Sep 17 00:00:00 2001 From: Jonas Hietala Date: Mon, 21 Jul 2014 15:20:57 +0200 Subject: [PATCH] Main example for priority queue using dijkstra's algorithm. --- src/libcollections/priority_queue.rs | 138 ++++++++++++++++++++++++++- 1 file changed, 137 insertions(+), 1 deletion(-) diff --git a/src/libcollections/priority_queue.rs b/src/libcollections/priority_queue.rs index 256621d08e54a..9451f2521c89a 100644 --- a/src/libcollections/priority_queue.rs +++ b/src/libcollections/priority_queue.rs @@ -8,7 +8,143 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! A priority queue implemented with a binary heap +//! A priority queue implemented with a binary heap. +//! +//! # Example +//! +//! This is a larger example which implements [Dijkstra's algorithm][dijkstra] +//! to solve the [shortest path problem][sssp] on a [directed graph][dir_graph]. +//! It showcases how to use the `PriorityQueue` with custom types. +//! +//! [dijkstra]: http://en.wikipedia.org/wiki/Dijkstra%27s_algorithm +//! [sssp]: http://en.wikipedia.org/wiki/Shortest_path_problem +//! [dir_graph]: http://en.wikipedia.org/wiki/Directed_graph +//! +//! ``` +//! use std::collections::PriorityQueue; +//! use std::uint; +//! +//! #[deriving(Eq, PartialEq)] +//! struct State { +//! cost: uint, +//! position: uint +//! } +//! +//! // The priority queue depends on `Ord`. +//! // Explicitly implement the trait so the queue becomes a min-heap +//! // instead of a max-heap. +//! impl Ord for State { +//! fn cmp(&self, other: &State) -> Ordering { +//! // Notice that the we flip the ordering here +//! other.cost.cmp(&self.cost) +//! } +//! } +//! +//! // `PartialOrd` needs to be implemented as well. +//! impl PartialOrd for State { +//! fn partial_cmp(&self, other: &State) -> Option { +//! Some(self.cmp(other)) +//! } +//! } +//! +//! // Each node is represented as an `uint`, for a shorter implementation. +//! struct Edge { +//! node: uint, +//! cost: uint +//! } +//! +//! // Dijkstra's shortest path algorithm. +//! +//! // Start at `start` and use `dist` to track the current shortest distance +//! // to each node. This implementation isn't memory efficient as it may leave duplicate +//! // nodes in the queue. It also uses `uint::MAX` as a sentinel value, +//! // for a simpler implementation. +//! fn shortest_path(adj_list: &Vec>, start: uint, goal: uint) -> uint { +//! // dist[node] = current shortest distance from `start` to `node` +//! let mut dist = Vec::from_elem(adj_list.len(), uint::MAX); +//! +//! let mut pq = PriorityQueue::new(); +//! +//! // We're at `start`, with a zero cost +//! *dist.get_mut(start) = 0u; +//! pq.push(State { cost: 0u, position: start }); +//! +//! // Examine the frontier with lower cost nodes first (min-heap) +//! loop { +//! let State { cost, position } = match pq.pop() { +//! None => break, // empty +//! Some(s) => s +//! }; +//! +//! // Alternatively we could have continued to find all shortest paths +//! if position == goal { return cost } +//! +//! // Important as we may have already found a better way +//! if cost > dist[position] { continue } +//! +//! // For each node we can reach, see if we can find a way with +//! // a lower cost going through this node +//! for edge in adj_list[position].iter() { +//! let next = State { cost: cost + edge.cost, position: edge.node }; +//! +//! // If so, add it to the frontier and continue +//! if next.cost < dist[next.position] { +//! pq.push(next); +//! // Relaxation, we have now found a better way +//! *dist.get_mut(next.position) = next.cost; +//! } +//! } +//! } +//! +//! // Goal not reachable +//! uint::MAX +//! } +//! +//! fn main() { +//! // This is the directed graph we're going to use. +//! // The node numbers correspond to the different states, +//! // and the edge weights symbolises the cost of moving +//! // from one node to another. +//! // Note that the edges are one-way. +//! // +//! // 7 +//! // +-----------------+ +//! // | | +//! // v 1 2 | +//! // 0 -----> 1 -----> 3 ---> 4 +//! // | ^ ^ ^ +//! // | | 1 | | +//! // | | | 3 | 1 +//! // +------> 2 -------+ | +//! // 10 | | +//! // +---------------+ +//! // +//! // The graph is represented as an adjecency list where each index, +//! // corresponding to a node value, has a list of outgoing edges. +//! // Chosen for it's efficiency. +//! let graph = vec![ +//! // Node 0 +//! vec![Edge { node: 2, cost: 10 }, +//! Edge { node: 1, cost: 1 }], +//! // Node 1 +//! vec![Edge { node: 3, cost: 2 }], +//! // Node 2 +//! vec![Edge { node: 1, cost: 1 }, +//! Edge { node: 3, cost: 3 }, +//! Edge { node: 4, cost: 1 }], +//! // Node 3 +//! vec![Edge { node: 0, cost: 7 }, +//! Edge { node: 4, cost: 2 }], +//! // Node 4 +//! vec![]]; +//! +//! assert_eq!(shortest_path(&graph, 0, 1), 1); +//! assert_eq!(shortest_path(&graph, 0, 3), 3); +//! assert_eq!(shortest_path(&graph, 3, 0), 7); +//! assert_eq!(shortest_path(&graph, 0, 4), 5); +//! assert_eq!(shortest_path(&graph, 4, 0), uint::MAX); +//! } +//! ``` #![allow(missing_doc)]