8000 Modernize Dijkstra implementation + add tests · cp-algorithms/cp-algorithms@d0feb5d · GitHub
[go: up one dir, main page]

Skip to content

Commit d0feb5d

Browse files
committed
Modernize Dijkstra implementation + add tests
1 parent c7daf1d commit d0feb5d

File tree

3 files changed

+84
-45
lines changed

3 files changed

+84
-45
lines changed

src/graph/dijkstra.md

Lines changed: 40 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,32 @@
11
<!--?title Dijkstra Algorithm -->
22

33
# Dijkstra Algorithm
4-
### Finding Shortest Paths from Given Vertex to all Other Vertices
54

6-
You are given a directed or undirected weighted graph with $n$ vertices and $m$ edges. The weights of all edges are non-negative. You are also given a starting vertex $s$. Find the lengths of the shortest paths from vertex $s$ to all other vertices, and output the shortest paths themselves.
5+
You are given a directed or undirected weighted graph with $n$ vertices and $m$ edges. The weights of all edges are non-negative. You are also given a starting vertex $s$. This article discusses finding the lengths of the shortest paths from a starting vertex $s$ to all other vertices, and output the shortest paths themselves.
76

8-
This problem is also called "single-source shortest paths problem".
7+
This problem is also called **single-source shortest paths problem**.
98

109
## Algorithm
1110

1211
Here is an algorithm described by the Dutch computer scientist Edsger W. Dijkstra in 1959.
1312

14-
Let's create an array $d[]$ where for each vertex $v$ we store the current length of the shortest path from $s$ to $v$ $d[v]$. Initially $d[s] = 0$, and for all other vertices this length equals infinity. In a computer implementation a sufficiently large number (which is guaranteed to be greater than any possible path length) is chosen as infinity:
13+
Let's create an array $d[]$ where for each vertex $v$ we store the current length of the shortest path from $s$ to $v$ in $d[v]$.
14+
Initially $d[s] = 0$, and for all other vertices this length equals infinity.
15+
In the implementation a sufficiently large number (which is guaranteed to be greater than any possible path length) is chosen as infinity.
1516

16-
$$d[v] = \infty, v \ne s$$
17+
$$d[v] = \infty,~ v \ne s$$
1718

18-
In addition, we maintain a boolean array $u[]$ which stores for each vertex $v$ whether it's marked. Initially all vertices are unmarked:
19+
In addition, we maintain a Boolean array $u[]$ which stores for each vertex $v$ whether it's marked. Initially all vertices are unmarked:
1920

2021
$$u[v] = {\rm false}$$
2122

2223
The Dijkstra's algorithm runs for $n$ iterations. At each iteration a vertex $v$ is chosen as unmarked vertex which has the least value $d[v]$:
2324

24-
$$d[v] = \min_{p: u[p]={\rm false}} d[p]$$
25-
2625
Evidently, in the first iteration the starting vertex $s$ will be selected.
2726

28-
Thus selected vertex $v$ is marked. Next, from vertex $v$ **relaxations** are performed: all edges of the form $(v,to)$ are considered, and for each vertex $to$ the algorithm tries to improve the value $d[to]$. If the length of the current edge equals $len$, the code for relaxation is:
27+
The selected vertex $v$ is marked. Next, from vertex $v$ **relaxations** are performed: all edges of the form $(v,\text{to})$ are considered, and for each vertex $\text{to}$ the algorithm tries to improve the value $d[\text{to}]$. If the length of the current edge equals $len$, the code for relaxation is:
2928

30-
$$d[to] = \min (d[to], d[v] + len)$$
29+
$$d[\text{to}] = \min (d[\text{to}], d[v] + len)$$
3130

3231
After all such edges are considered, the current iteration ends. Finally, after $n$ iterations, all vertices will be marked, and the algorithm terminates. We claim that the found values $d[v]$ are the lengths of shortest paths from $s$ to all vertices $v$.
3332

@@ -39,9 +38,9 @@ Usually one needs to know not only the lengths of shortest paths but also the sh
3938

4039
$$P = (s, \ldots, p[p[p[v]]], p[p[v]], p[v], v)$$
4140

42-
Building this array of predecessors is very simple: for each successful relaxation, i.e. when for some selected vertex $v$, there is an improvement in the distance to some vertex $to$, we update the predecessor vertex for $to$ with vertex $v$:
41+
Building this array of predecessors is very simple: for each successful relaxation, i.e. when for some selected vertex $v$, there is an improvement in the distance to some vertex $\text{to}$, we update the predecessor vertex for $\text{to}$ with vertex $v$:
4342

44-
$$p[to] = v$$
43+
$$p[\text{to}] = v$$
4544

4645
## Proof
4746

@@ -71,7 +70,7 @@ Q.E.D.
7170

7271
## Implementation
7372

74-
Dijkstra's algorithm performs $n$ iterations. On each iteration it selects an unmarked vertex $v$ with the lowest value $d[v]$, marks it and checks all the edges $(v, to)$ attempting to improve the value $d[to]$.
73+
Dijkstra's algorithm performs $n$ iterations. On each iteration it selects an unmarked vertex $v$ with the lowest value $d[v]$, marks it and checks all the edges $(v, \text{to})$ attempting to improve the value $d[\text{to}]$.
7574

7675
The running time of the algorithm consists of:
7776

@@ -82,40 +81,31 @@ For the simplest implementation of these operations on each iteration vertex sea
8281

8382
$$O(n^2+m)$$
8483

85-
```cpp
84+
```cpp dijkstra_dense
8685
const int INF = 1000000000;
86+
vector<vector<pair<int, int>>> adj;
87+
88+
void dijkstra(int s, vector<int> & d, vector<int> & p) {
89+
int n = adj.size();
90+
d.assign(n, INF);
91+
p.assign(n, -1);
92+
vector<bool> u(n, false);
8793

88-
int main() {
89-
int n;
90-
/*... read n ...*/
91-
92-
vector<vector<pair<int, int > > > g(n);
93-
/*... read graph ...*/
94-
95-
int s = ...; // read starting vertex
96-
97-
vector<int> d(n, INF), p(n);
98-
9994
d[s] = 0;
100-
101-
vector<char> u(n);
102-
103-
for (int i = 0; i < n; ++i) {
95+
for (int i = 0; i < n; i++) {
10496
int v = -1;
105-
106-
for (int j = 0; j < n; ++j)
97+
for (int j = 0; j < n; j++) {
10798
if (!u[j] && (v == -1 || d[j] < d[v]))
10899
v = j;
100+
}
109101
110102
if (d[v] == INF)
111103
break;
112104
113105
u[v] = true;
114-
115-
for (size_t j = 0; j < g[v].size(); ++j) {
116-
117-
int to = g[v][j].first,
118-
int len = g[v][j].second;
106+
for (auto edge : adj[v]) {
107+
int to = edge.first;
108+
int len = edge.second;
119109
120110
if (d[v] + len < d[to]) {
121111
d[to] = d[v] + len;
@@ -126,20 +116,25 @@ int main() {
126116
}
127117
```
128118
129-
Here the graph $g$ is stored as adjacency list: for each vertex $v$ $g[v]$ contains the list of edges going from this vertex, i.e. the list of `pair<int,int>` where the first element in the pair is the vertex at the other end of the edge, and the second element is the edge weight.
119+
Here the graph $\text{adj}$ is stored as adjacency list: for each vertex $v$ $\text{adj}[v]$ contains the list of edges going from this vertex, i.e. the list of `pair<int,int>` where the first element in the pair is the vertex at the other end of the edge, and the second element is the edge weight.
130120
131-
First of all, the code initializes arrays: distances $d[]$, labels $u[]$ and predecessors $p[]$. Then it performs $n$ iterations. At each iteration the vertex $v$ is selected which has the smallest distance $d[v]$ among all the unmarked vertices. If the distance to selected vertex $v$ is equal to infinity, the algorithm stops. Otherwise the vertex is marked, and all the edges going out from this vertex are checked. If relaxation along the edge is possible (i.e. distance $d[to]$ can be improved), the distance $d[to]$ and predecessor $p[to]$ are updated.
121+
The function takes the starting vertex $s$ and two vectors that will be used as return values.
122+
123+
First of all, the code initializes arrays: distances $d[]$, labels $u[]$ and predecessors $p[]$. Then it performs $n$ iterations. At each iteration the vertex $v$ is selected which has the smallest distance $d[v]$ among all the unmarked vertices. If the distance to selected vertex $v$ is equal to infinity, the algorithm stops. Otherwise the vertex is marked, and all the edges going out from this vertex are checked. If relaxation along the edge is possible (i.e. distance $d[\text{to}]$ can be improved), the distance $d[\text{to}]$ and predecessor $p[\text{to}]$ are updated.
132124
133125
After performing all the iterations array $d[]$ stores the lengths of the shortest paths to all vertices, and array $p[]$ stores the predecessors of all vertices (except starting vertex $s$). The path to any vertex $t$ can be restored in the following way:
134126
135-
```cpp
136-
vector<int> path;
127+
```cpp dijkstra_restore_path
128+
vector<int> restore_path(int s, int t, vector<int> const& p) {
129+
vector<int> path;
137130
138-
for (int v = t; v != s; v = p[v])
139-
path.push_back(v);
140-
path.push_back(s);
131+
for (int v = t; v != s; v = p[v])
132+
path.push_back(v);
133+
path.push_back(s);
141134
142-
reverse(path.begin(), path.end());
135+
reverse(path.begin(), path.end());
136+
return path;
137+
}
143138
```
144139

145140
## References
@@ -180,7 +175,7 @@ reverse(path.begin(), path.end());
180175
* [UVA 11374 - Airport Express](https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2369)
181176
* [UVA 11097 - Poor My Problem](https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2038)
182177
* [UVA 13172 - The music teacher](https://uva.onlinejudge.org/index.php?option=onlinejudge&Itemid=8&page=show_problem&problem=5083)
183-
* [Codeforces - Dirty Arkadys Kichen](http://codeforces.com/contest/827/problem/F)
178+
* [Codeforces - Dirty Arkady's Kitchen](http://codeforces.com/contest/827/problem/F)
184179
* [SPOJ - Delivery Route](http://www.spoj.com/problems/DELIVER/)
185180
* [SPOJ - Costly Chess](http://www.spoj.com/problems/CCHESS/)
186181

test/data/sssp.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#include <vector>
2+
using namespace std;
3+
4+
struct Graph_SSSP {
5+
vector<vector<pair<int, int>>> adj;
6+
int s;
7+
vector<int> expected_d;
8+
vector<int> expected_p;
9+
};
10+
11+
vector<Graph_SSSP> sssp_graphs = {
12+
// wikipedia
13+
{
14+
{
15+
{{1, 7}, {2, 9}, {5, 14}},
16+
{{0, 7}, {2, 10}, {3, 15}},
17+
{{0, 9}, {1, 10}, {3, 11}, {5, 2}},
18+
{{1, 15}, {2, 11}, {4, 6}},
19+
{{3, 6}, {5, 9}},
20+
{{0, 14}, {2, 2}, {4, 9}}
21+
},
22+
0,
23+
{0, 7, 9, 20, 20, 11},
24+
{-1, 0, 0, 2, 5, 2}
25+
}
26+
};

test/test_dijkstra.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#include <cassert>
2+
#include <vector>
3+
#include <algorithm>
4+
using namespace std;
5+
6+
#include "dijkstra_dense.h"
7+
#include "dijkstra_restore_path.h"
8+
#include "data/sssp.h"
9+
10+
int main() {
11+
for (auto const& graph : sssp_graphs) {
12+
adj = graph.adj;
13+
vector<int> d, p;
14+
dijkstra(graph.s, d, p);
15+
assert(d == graph.expected_d);
16+
assert(p == graph.expected_p);
17+
}
18+
}

0 commit comments

Comments
 (0)
0