-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
/
dijkstra.rs
155 lines (130 loc) · 5.08 KB
/
dijkstra.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
use std::collections::BTreeMap;
use std::ops::Add;
type Graph<V, E> = BTreeMap<V, BTreeMap<V, E>>;
// performs Dijsktra's algorithm on the given graph from the given start
// the graph is a positively-weighted undirected graph
//
// returns a map that for each reachable vertex associates the distance and the predecessor
// since the start has no predecessor but is reachable, map[start] will be None
//
// Time: O(E * logV). For each vertex, we traverse each edge, resulting in O(E). For each edge, we
// insert a new shortest path for a vertex into the tree, resulting in O(E * logV).
// Space: O(V). The tree holds up to V vertices.
pub fn dijkstra<V: Ord + Copy, E: Ord + Copy + Add<Output = E>>(
graph: &Graph<V, E>,
start: V,
) -> BTreeMap<V, Option<(V, E)>> {
let mut ans = BTreeMap::new();
let mut prio = BTreeMap::new();
// start is the special case that doesn't have a predecessor
ans.insert(start, None);
for (new, weight) in &graph[&start] {
ans.insert(*new, Some((start, *weight)));
prio.insert(*new, *weight);
}
while let Some((vertex, path_weight)) = prio.pop_first() {
for (next, weight) in &graph[&vertex] {
let new_weight = path_weight + *weight;
match ans.get(next) {
// if ans[next] is a lower dist than the alternative one, we do nothing
Some(Some((_, dist_next))) if new_weight >= *dist_next => {}
// if ans[next] is None then next is start and so the distance won't be changed, it won't be added again in prio
Some(None) => {}
// the new path is shorter, either new was not in ans or it was farther
_ => {
ans.insert(*next, Some((vertex, new_weight)));
prio.insert(*next, new_weight);
}
}
}
}
ans
}
#[cfg(test)]
mod tests {
use super::{dijkstra, Graph};
use std::collections::BTreeMap;
fn add_edge<V: Ord + Copy, E: Ord>(graph: &mut Graph<V, E>, v1: V, v2: V, c: E) {
graph.entry(v1).or_default().insert(v2, c);
graph.entry(v2).or_default();
}
#[test]
fn single_vertex() {
let mut graph: Graph<usize, usize> = BTreeMap::new();
graph.insert(0, BTreeMap::new());
let mut dists = BTreeMap::new();
dists.insert(0, None);
assert_eq!(dijkstra(&graph, 0), dists);
}
#[test]
fn single_edge() {
let mut graph = BTreeMap::new();
add_edge(&mut graph, 0, 1, 2);
let mut dists_0 = BTreeMap::new();
dists_0.insert(0, None);
dists_0.insert(1, Some((0, 2)));
assert_eq!(dijkstra(&graph, 0), dists_0);
let mut dists_1 = BTreeMap::new();
dists_1.insert(1, None);
assert_eq!(dijkstra(&graph, 1), dists_1);
}
#[test]
fn tree_1() {
let mut graph = BTreeMap::new();
let mut dists = BTreeMap::new();
dists.insert(1, None);
for i in 1..100 {
add_edge(&mut graph, i, i * 2, i * 2);
add_edge(&mut graph, i, i * 2 + 1, i * 2 + 1);
match dists[&i] {
Some((_, d)) => {
dists.insert(i * 2, Some((i, d + i * 2)));
dists.insert(i * 2 + 1, Some((i, d + i * 2 + 1)));
}
None => {
dists.insert(i * 2, Some((i, i * 2)));
dists.insert(i * 2 + 1, Some((i, i * 2 + 1)));
}
}
}
assert_eq!(dijkstra(&graph, 1), dists);
}
#[test]
fn graph_1() {
let mut graph = BTreeMap::new();
add_edge(&mut graph, 'a', 'c', 12);
add_edge(&mut graph, 'a', 'd', 60);
add_edge(&mut graph, 'b', 'a', 10);
add_edge(&mut graph, 'c', 'b', 20);
add_edge(&mut graph, 'c', 'd', 32);
add_edge(&mut graph, 'e', 'a', 7);
let mut dists_a = BTreeMap::new();
dists_a.insert('a', None);
dists_a.insert('c', Some(('a', 12)));
dists_a.insert('d', Some(('c', 44)));
dists_a.insert('b', Some(('c', 32)));
assert_eq!(dijkstra(&graph, 'a'), dists_a);
let mut dists_b = BTreeMap::new();
dists_b.insert('b', None);
dists_b.insert('a', Some(('b', 10)));
dists_b.insert('c', Some(('a', 22)));
dists_b.insert('d', Some(('c', 54)));
assert_eq!(dijkstra(&graph, 'b'), dists_b);
let mut dists_c = BTreeMap::new();
dists_c.insert('c', None);
dists_c.insert('b', Some(('c', 20)));
dists_c.insert('d', Some(('c', 32)));
dists_c.insert('a', Some(('b', 30)));
assert_eq!(dijkstra(&graph, 'c'), dists_c);
let mut dists_d = BTreeMap::new();
dists_d.insert('d', None);
assert_eq!(dijkstra(&graph, 'd'), dists_d);
let mut dists_e = BTreeMap::new();
dists_e.insert('e', None);
dists_e.insert('a', Some(('e', 7)));
dists_e.insert('c', Some(('a', 19)));
dists_e.insert('d', Some(('c', 51)));
dists_e.insert('b', Some(('c', 39)));
assert_eq!(dijkstra(&graph, 'e'), dists_e);
}
}