|
8 | 8 | // option. This file may not be copied, modified, or distributed
|
9 | 9 | // except according to those terms.
|
10 | 10 |
|
11 |
| -//! A priority queue implemented with a binary heap |
| 11 | +//! A priority queue implemented with a binary heap. |
| 12 | +//! |
| 13 | +//! # Example |
| 14 | +//! |
| 15 | +//! This is a larger example which implements [Dijkstra's algorithm][dijkstra] |
| 16 | +//! to solve the [shortest path problem][sssp] on a [directed graph][dir_graph]. |
| 17 | +//! It showcases how to use the `PriorityQueue` with custom types. |
| 18 | +//! |
| 19 | +//! [dijkstra]: http://en.wikipedia.org/wiki/Dijkstra%27s_algorithm |
| 20 | +//! [sssp]: http://en.wikipedia.org/wiki/Shortest_path_problem |
| 21 | +//! [dir_graph]: http://en.wikipedia.org/wiki/Directed_graph |
| 22 | +//! |
| 23 | +//! ``` |
| 24 | +//! use std::collections::PriorityQueue; |
| 25 | +//! use std::uint; |
| 26 | +//! |
| 27 | +//! #[deriving(Eq, PartialEq)] |
| 28 | +//! struct State { |
| 29 | +//! cost: uint, |
| 30 | +//! position: uint |
| 31 | +//! } |
| 32 | +//! |
| 33 | +//! // The priority queue depends on `Ord`. |
| 34 | +//! // Explicitly implement the trait so the queue becomes a min-heap |
| 35 | +//! // instead of a max-heap. |
| 36 | +//! impl Ord for State { |
| 37 | +//! fn cmp(&self, other: &State) -> Ordering { |
| 38 | +//! // Notice that the we flip the ordering here |
| 39 | +//! other.cost.cmp(&self.cost) |
| 40 | +//! } |
| 41 | +//! } |
| 42 | +//! |
| 43 | +//! // `PartialOrd` needs to be implemented as well. |
| 44 | +//! impl PartialOrd for State { |
| 45 | +//! fn partial_cmp(&self, other: &State) -> Option<Ordering> { |
| 46 | +//! Some(self.cmp(other)) |
| 47 | +//! } |
| 48 | +//! } |
| 49 | +//! |
| 50 | +//! // Each node is represented as an `uint`, for a shorter implementation. |
| 51 | +//! struct Edge { |
| 52 | +//! node: uint, |
| 53 | +//! cost: uint |
| 54 | +//! } |
| 55 | +//! |
| 56 | +//! // Dijkstra's shortest path algorithm. |
| 57 | +//! |
| 58 | +//! // Start at `start` and use `dist` to track the current shortest distance |
| 59 | +//! // to each node. This implementation isn't memory efficient as it may leave duplicate |
| 60 | +//! // nodes in the queue. It also uses `uint::MAX` as a sentinel value, |
| 61 | +//! // for a simpler implementation. |
| 62 | +//! fn shortest_path(adj_list: &Vec<Vec<Edge>>, start: uint, goal: uint) -> uint { |
| 63 | +//! // dist[node] = current shortest distance from `start` to `node` |
| 64 | +//! let mut dist = Vec::from_elem(adj_list.len(), uint::MAX); |
| 65 | +//! |
| 66 | +//! let mut pq = PriorityQueue::new(); |
| 67 | +//! |
| 68 | +//! // We're at `start`, with a zero cost |
| 69 | +//! *dist.get_mut(start) = 0u; |
| 70 | +//! pq.push(State { cost: 0u, position: start }); |
| 71 | +//! |
| 72 | +//! // Examine the frontier with lower cost nodes first (min-heap) |
| 73 | +//! loop { |
| 74 | +//! let State { cost, position } = match pq.pop() { |
| 75 | +//! None => break, // empty |
| 76 | +//! Some(s) => s |
| 77 | +//! }; |
| 78 | +//! |
| 79 | +//! // Alternatively we could have continued to find all shortest paths |
| 80 | +//! if position == goal { return cost } |
| 81 | +//! |
| 82 | +//! // Important as we may have already found a better way |
| 83 | +//! if cost > dist[position] { continue } |
| 84 | +//! |
| 85 | +//! // For each node we can reach, see if we can find a way with |
| 86 | +//! // a lower cost going through this node |
| 87 | +//! for edge in adj_list[position].iter() { |
| 88 | +//! let next = State { cost: cost + edge.cost, position: edge.node }; |
| 89 | +//! |
| 90 | +//! // If so, add it to the frontier and continue |
| 91 | +//! if next.cost < dist[next.position] { |
| 92 | +//! pq.push(next); |
| 93 | +//! // Relaxation, we have now found a better way |
| 94 | +//! *dist.get_mut(next.position) = next.cost; |
| 95 | +//! } |
| 96 | +//! } |
| 97 | +//! } |
| 98 | +//! |
| 99 | +//! // Goal not reachable |
| 100 | +//! uint::MAX |
| 101 | +//! } |
| 102 | +//! |
| 103 | +//! fn main() { |
| 104 | +//! // This is the directed graph we're going to use. |
| 105 | +//! // The node numbers correspond to the different states, |
| 106 | +//! // and the edge weights symbolises the cost of moving |
| 107 | +//! // from one node to another. |
| 108 | +//! // Note that the edges are one-way. |
| 109 | +//! // |
| 110 | +//! // 7 |
| 111 | +//! // +-----------------+ |
| 112 | +//! // | | |
| 113 | +//! // v 1 2 | |
| 114 | +//! // 0 -----> 1 -----> 3 ---> 4 |
| 115 | +//! // | ^ ^ ^ |
| 116 | +//! // | | 1 | | |
| 117 | +//! // | | | 3 | 1 |
| 118 | +//! // +------> 2 -------+ | |
| 119 | +//! // 10 | | |
| 120 | +//! // +---------------+ |
| 121 | +//! // |
| 122 | +//! // The graph is represented as an adjecency list where each index, |
| 123 | +//! // corresponding to a node value, has a list of outgoing edges. |
| 124 | +//! // Chosen for it's efficiency. |
| 125 | +//! let graph = vec![ |
| 126 | +//! // Node 0 |
| 127 | +//! vec![Edge { node: 2, cost: 10 }, |
| 128 | +//! Edge { node: 1, cost: 1 }], |
| 129 | +//! // Node 1 |
| 130 | +//! vec![Edge { node: 3, cost: 2 }], |
| 131 | +//! // Node 2 |
| 132 | +//! vec![Edge { node: 1, cost: 1 }, |
| 133 | +//! Edge { node: 3, cost: 3 }, |
| 134 | +//! Edge { node: 4, cost: 1 }], |
| 135 | +//! // Node 3 |
| 136 | +//! vec![Edge { node: 0, cost: 7 }, |
| 137 | +//! Edge { node: 4, cost: 2 }], |
| 138 | +//! // Node 4 |
| 139 | +//! vec![]]; |
| 140 | +//! |
| 141 | +//! assert_eq!(shortest_path(&graph, 0, 1), 1); |
| 142 | +//! assert_eq!(shortest_path(&graph, 0, 3), 3); |
| 143 | +//! assert_eq!(shortest_path(&graph, 3, 0), 7); |
| 144 | +//! assert_eq!(shortest_path(&graph, 0, 4), 5); |
| 145 | +//! assert_eq!(shortest_path(&graph, 4, 0), uint::MAX); |
| 146 | +//! } |
| 147 | +//! ``` |
12 | 148 |
|
13 | 149 | #![allow(missing_doc)]
|
14 | 150 |
|
|
0 commit comments