Skip to content

Commit d5fcd98

Browse files
committed
add docs + rename direction -> orientation + minor changes
1 parent 810b419 commit d5fcd98

File tree

1 file changed

+169
-30
lines changed

1 file changed

+169
-30
lines changed

src/dijkstra.hpp

Lines changed: 169 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -19,104 +19,219 @@
1919

2020
/*
2121
* @author Antoine Sébert
22-
* @description Implementation of Dijkstra's algorithm in C++17 as a single
23-
* header library using a fibonacci heap
22+
* @description Implementation of Dijkstra's algorithm in C++17 as a single header library using a fibonacci heap.
2423
*/
2524
namespace dijkstra {
2625
using namespace std;
2726
using namespace boost::heap;
2827
namespace fs = std::filesystem;
2928

30-
enum class Direction : uint8_t {
31-
DIRECTED,
32-
UNDIRECTED,
29+
/*
30+
* The orientation of a graph. Has an impact on edge insertion and deletion.
31+
*/
32+
enum class Orientation : uint8_t {
33+
ORIENTED,
34+
UNORIENTED,
3335
};
3436

35-
template <typename W, class = enable_if_t<is_integral_v<W>>>
37+
/*
38+
* A simple Graph class.
39+
* - Orientation : The graph is either oriented or not.
40+
* In reality all egdes are oriented, but the add_edge() and add_node() methods uses oriented edges to simulate unorientation.
41+
* - Weights : The graph can contain weighted edges, unweighted edges, or a mix of both, depending on the arguments passed to add_edge() and add_node().
42+
* Under the hood all
43+
*
44+
* @param W An integral type for the edge weights, that supports numeric_limits<>::max(), addition and comparison.
45+
*/
46+
template <typename W = uint_fast32_t, class = enable_if_t<is_integral_v<W>>>
3647
class Graph {
3748
public:
49+
/*
50+
* A simple Node class, essentially a wrapper around edges.
51+
*/
3852
struct Node {
3953
map<string, W> neighbors = {};
4054
};
55+
56+
/*
57+
* The comparator for the items in the fibonacci heap.
58+
*/
4159
struct comparator {
42-
bool operator() (const pair<string, W>& lhs, const pair<string, W>& rhs) const {
60+
inline bool operator() (const pair<string, W>& lhs, const pair<string, W>& rhs) const {
4361
return get<1>(lhs) > get<1>(rhs);
4462
}
4563
};
4664
private:
4765
map<string, Node> nodes;
48-
Direction dir;
66+
Orientation _or;
4967

5068
public:
51-
Graph(Direction _dir = Direction::UNDIRECTED) noexcept : nodes({}), dir(_dir) {}
69+
/*
70+
* Constructor.
71+
*
72+
* @param _or the edge orientation
73+
*/
74+
Graph(Orientation _or = Orientation::UNORIENTED) noexcept : nodes({}), _or(_or) {}
75+
76+
/* NODES */
77+
78+
/*
79+
* Returns the underlying node container.
80+
*
81+
* @return a map of strings to nodes
82+
*/
83+
inline const map<string, Node>& get_nodes() const noexcept {
84+
return nodes;
85+
}
5286

53-
Direction get_direction() const noexcept {
54-
return dir;
87+
/*
88+
* Check if a node exists.
89+
*
90+
* @param label the label
91+
* @return true if the node exists, or false otherwise
92+
*/
93+
inline bool contains(string label) const noexcept {
94+
return nodes.find(label) != nodes.end();
5595
}
5696

57-
const map<string, Node>& get_nodes() const noexcept {
58-
return nodes;
97+
/*
98+
* Check if the graph is empty.
99+
*
100+
* @return true if the node container is empty, or false otherwise
101+
*/
102+
inline bool empty() const noexcept {
103+
return nodes.empty();
59104
}
60105

106+
/*
107+
* Adds a node. Idempotent.
108+
*
109+
* @param label the label
110+
*/
61111
void add_node(string label) {
62112
if (!contains(label))
63113
nodes[label] = {};
64114
}
65115

116+
/*
117+
* Adds a node and the weighted egdes between neighbors. All non-existing neighbors will be created on the fly.
118+
*
119+
* @param label the label
120+
* @param neighbors the edges
121+
*
122+
* @return this
123+
*/
66124
Graph add_node(string label, const map<string, W>& neighbors) {
67125
add_node(label);
68126

69127
for (const auto& [neighbor, weight] : neighbors)
70128
add_edge(label, neighbor, weight);
71129

72-
if (dir == Direction::UNDIRECTED)
130+
if (_or == Orientation::UNORIENTED)
73131
for (const auto& [neighbor, weight] : neighbors)
74132
add_edge(neighbor, label, weight);
133+
134+
return this;
75135
}
76136

137+
/*
138+
* Adds a node and the weighted egdes between neighbors. All non-existing neighbors will be created on the fly. All edge weights are defaulted to 0 (can emulate unweighted graphs).
139+
*
140+
* @param label the label
141+
* @param neighbors the edges
142+
*
143+
* @return this
144+
*/
145+
Graph add_node(string label, const set<string>& neighbors) {
146+
auto _neighbors = map<string, W>();
147+
148+
for (const auto& _label : neighbors)
149+
_neighbors[_label] = 0;
150+
151+
return add_node(label, neighbors);
152+
}
153+
154+
/*
155+
* Removes a node and any edge pointing to it.
156+
*
157+
* @param label the label
158+
*/
77159
void remove_node(string label) {
78160
nodes.erase(label);
79161

80162
for (auto& [_, node] : nodes)
81163
node.neighbors.erase(label);
82164
}
83165

166+
/* EDGES */
167+
168+
/*
169+
* Returns the orientation.
170+
*/
171+
inline Orientation get_orientation() const noexcept {
172+
return _or;
173+
}
174+
175+
/*
176+
* Adds an edge, whose weight is optional and defaults to 0.
177+
* If the graph is oriented, the order in wich the node labels are specified is significant; non-existing nodes will be created on the fly.
178+
*
179+
* @param label0 the label of the first node
180+
* @param label1 the label of the second node
181+
* @note Self-edges are authorised
182+
*/
84183
void add_edge(string label0, string label1, W weight = W(0)) {
85184
add_node(label0);
86185
add_node(label1);
87186

88187
nodes.at(label0).neighbors[label1] = weight;
89188

90-
if (dir == Direction::UNDIRECTED)
189+
if (_or == Orientation::UNORIENTED)
91190
nodes.at(label1).neighbors[label0] = weight;
92191
}
93192

193+
/*
194+
* Removes an edge.
195+
*
196+
* @param label0 the label of the first node
197+
* @param label1 the label of the second node
198+
* @return true if the edge exists and has been removed, or false otherwise
199+
*/
94200
bool remove_edge(string label0, string label1) {
95201
if (has_neighbor(label0, label1)) {
96202
nodes.at(label0).neighbors.erase(label1);
97203

98-
if (dir == Direction::UNDIRECTED && has_neighbor(label1, label0))
204+
if (_or == Orientation::UNORIENTED && has_neighbor(label1, label0))
99205
nodes.at(label1).neighbors.erase(label0);
100206

101207
return true;
102208
}
103-
104-
return false;
105-
}
106-
107-
inline bool contains(string label) const {
108-
return nodes.find(label) != nodes.end();
109-
}
110-
111-
bool empty() const {
112-
return nodes.empty();
209+
else
210+
return false;
113211
}
114212

213+
/*
214+
* Checks if two nodes are neighbors
215+
*
216+
* @param label0 the label of the first node
217+
* @param label1 the label of the second node
218+
* @return true if the two nodes exist and an edge between the nodes exist, or false otherwise
219+
*/
115220
bool has_neighbor(string label0, string label1) const {
116221
return contains(label0) && contains(label1)
117222
&& nodes.at(label0).neighbors.find(label1) != nodes.at(label0).neighbors.end();
118223
}
119224

225+
/* DIJKSTRA'S ALGORITHM */
226+
227+
/*
228+
* Finds the shortest path between two nodes, asserting it exists.
229+
*
230+
* @param src the label of the source node
231+
* @param dst the label of the destination node
232+
* @return a path that starts with src and ends with dst
233+
* @cite @article{dijkstra1959note, title={A note on two problems in connexion with graphs}, author={Dijkstra, Edsger W and others}, journal={Numerische mathematik}, volume={1}, number={1}, pages={269--271}, year={1959}}
234+
*/
120235
fs::path find(string src, string dst) const {
121236
assertions(src, dst);
122237

@@ -148,16 +263,40 @@ namespace dijkstra {
148263
}
149264

150265
private:
151-
W get_distance(fibonacci_heap<pair<string, W>, compare<comparator>>& heap, string label) const {
152-
return find_if(heap.begin(), heap.end(), [&](const auto& e) { return get<0>(e) == label; })->second;
266+
/*
267+
* Looks up the distance associated to a label in a heap.
268+
*
269+
* @param heap the heap
270+
* @param label the label
271+
* @return the distance of the node if it exists, or numeric_limits<W>::max() otherwise
272+
*/
273+
inline W get_distance(fibonacci_heap<pair<string, W>, compare<comparator>>& heap, string label) const {
274+
auto it = find_if(heap.begin(), heap.end(), [&](const auto& e) { return get<0>(e) == label; });
275+
276+
return it != heap.end() ? it->second : numeric_limits<W>::max();
153277
}
154-
void assertions(string src, string dst) const {
278+
279+
/*
280+
* Performs quick assertions to ensure the search for the shortest path can start (but not if it will succeed).
281+
*
282+
* @param src the label of the source node
283+
* @param dst the label of the destination node
284+
*/
285+
inline void assertions(string src, string dst) const {
155286
assert(("The graph is empty", !empty()));
156287
assert(("The source node is not in the graph", contains(src)));
157288
assert(("The destination node is not in the graph", contains(dst)));
158-
assert(("The source and destinatino are the same", dst != src));
289+
assert(("The source and destination are the same", dst != src));
159290
}
160291

292+
/*
293+
* Transforms the predecessors into a path.
294+
*
295+
* @param preds the map of node predecessors
296+
* @param src the label of the source node
297+
* @param dst the label of the destination node
298+
* @return a path from src to dst
299+
*/
161300
fs::path preds_to_path(const map<string, string>& preds, string src, string dst) const {
162301
vector<string> reverse_path = { dst };
163302
while (reverse_path.back() != src)

0 commit comments

Comments
 (0)