1
1
#ifndef GRAPHS_CPP_GRAPH_H
2
2
#define GRAPHS_CPP_GRAPH_H
3
3
4
+ #include < utility>
4
5
#include < vector>
5
6
#include < sstream>
6
7
#include < algorithm>
9
10
10
11
namespace graph {
11
12
13
+ // Interface of a graph
12
14
class Graph {
13
15
public:
14
- virtual void addEdge (int v, int w) = 0; // add edge v-w
15
- [[nodiscard]] virtual const std::vector<int >& adj (int v) const = 0; // get edges adjacent to v
16
+ virtual void addEdge (int v, int w) = 0; // add edge v-w to graph
17
+ [[nodiscard]] virtual const std::vector<int >& adj (int v) const = 0; // get vertices adjacent to v
16
18
[[nodiscard]] virtual int V () const = 0; // number of vertices
17
19
[[nodiscard]] virtual int E () const = 0; // number of edges
18
20
[[nodiscard]] virtual std::string toString () const = 0; // create string representation
19
21
[[nodiscard]] virtual bool vertexValid (int v) const = 0; // whether a vertex exists
20
22
virtual ~Graph () = default ; // virtual destructor
21
23
};
22
24
25
+ // Graph built with adjacency list
23
26
class AdjacencyListGraph : public Graph {
24
27
public:
25
28
// create graph with V vertices
26
29
explicit AdjacencyListGraph (int V) :
27
30
numV(V),
28
31
numE(0 ),
29
- vertices (V, std::vector<int >{}) {}
32
+ adjacencies (V, std::vector<int >{}) {}
30
33
31
34
// create graph with input stream
32
35
explicit AdjacencyListGraph (std::istream &is) {
33
36
if (!(is >> numV) || numV < 0 ) throw std::invalid_argument (" Invalid format (V)" );
34
37
if (!(is >> numE || numE < 0 )) throw std::invalid_argument (" Invalid format (E)" );
35
- vertices .insert (vertices .end (), numV, {});
38
+ adjacencies .insert (adjacencies .end (), numV, {});
36
39
for (int i = 0 ; i < numE; ++i) {
37
40
int v1, v2;
38
41
if (!(is >> v1 >> v2)) throw std::invalid_argument (" Invalid format (edges)" );
39
42
if (v1 < 0 || v1 >= numV || v2 < 0 || v2 >= numV) {
40
43
throw std::invalid_argument (" Cannot create edge v1-v2" );
41
44
}
42
- vertices [v1].push_back (v2);
43
- vertices [v2].push_back (v1);
45
+ adjacencies [v1].push_back (v2);
46
+ adjacencies [v2].push_back (v1);
44
47
}
45
48
}
46
49
50
+ // add edge v-w to graph
47
51
void addEdge (int v, int w) override {
48
52
if (!vertexValid (v) || !vertexValid (w)) {
49
53
throw std::invalid_argument (" Cannot create edge v-w" );
50
54
}
51
- vertices [v].push_back (w);
52
- vertices [w].push_back (v);
55
+ adjacencies [v].push_back (w);
56
+ adjacencies [w].push_back (v);
53
57
++numE;
54
58
}
55
59
60
+ // get vertices adjacent to v
56
61
[[nodiscard]] const std::vector<int >& adj (int v) const override {
57
62
if (!vertexValid (v)) {
58
63
throw std::invalid_argument (" Invalid vertex" );
59
64
}
60
- return vertices [v];
65
+ return adjacencies [v];
61
66
}
62
67
68
+ // number of vertices
63
69
[[nodiscard]] int V () const override {
64
70
return numV;
65
71
}
66
72
73
+ // number of edges
67
74
[[nodiscard]] int E () const override {
68
75
return numE;
69
76
}
70
77
78
+ // create string representation
71
79
[[nodiscard]] std::string toString () const override {
72
80
std::stringstream ss;
73
81
ss << " [Graph with " << numV << " vertices and " << numE << " edges]\n " ;
74
- for (size_t i = 0 ; i < vertices .size (); ++i) {
75
- for (const auto edge : vertices [i]) {
82
+ for (size_t i = 0 ; i < adjacencies .size (); ++i) {
83
+ for (const auto edge : adjacencies [i]) {
76
84
ss << i << " -" << edge << " \n " ;
77
85
}
78
86
}
79
87
return ss.str ();
80
88
}
81
89
90
+ // whether a vertex exists
82
91
[[nodiscard]] bool vertexValid (int v) const override {
83
92
return v >= 0 && v < numV;
84
93
}
85
94
86
95
private:
87
96
int numV = 0 ; // vertices
88
97
int numE = 0 ; // edges
89
- std::vector<std::vector<int >> vertices {}; // vector with V elements each of size degree(vertex) to represent edges
98
+ std::vector<std::vector<int >> adjacencies {}; // vector with V elements each of size degree(vertex) to represent edges
90
99
91
100
};
92
101
@@ -122,22 +131,47 @@ namespace graph {
122
131
return num/2 ; // each self-loop is counted twice
123
132
}
124
133
125
- // Depth First Search for paths starting at a specific vertex
126
- class PathsFromDFS {
134
+ namespace internal {
135
+ // return whether element v exists in a collection col
136
+ template <typename Col>
137
+ [[nodiscard]] inline bool validIndex (const Col& col, const int v) {
138
+ if (v < 0 || static_cast <size_t >(v) >= col.size ()) {
139
+ return false ;
140
+ } else {
141
+ return true ;
142
+ }
143
+ }
144
+
145
+ } // namespace internal
146
+
147
+ // Result of a search for all paths reachable from a specific vortex
148
+ class PathsFromVertexResult {
127
149
public:
128
- explicit PathsFromDFS (const Graph &g, const int _start) :
129
- start {_start}, marked(g.V(), false ), edgeTo(g.V(), -1 ) {
130
- dfs (g, start);
150
+ // Construct by copying fields
151
+ explicit PathsFromVertexResult (int _start, const std::vector<int >& _distTo, const std::vector<int >& _edgeTo) // NOLINT
152
+ :
153
+ start(_start), distTo(_distTo), edgeTo(_edgeTo) {}
154
+
155
+ // Construct by moving fields
156
+ explicit PathsFromVertexResult (int _start, std::vector<int >&& _distTo, std::vector<int >&& _edgeTo)
157
+ :
158
+ start(_start), distTo(std::move(_distTo)), edgeTo(std::move(_edgeTo)) {}
159
+
160
+ // from which vertex the search was started
161
+ [[nodiscard]] int from () const {
162
+ return start;
131
163
}
132
164
133
- [[nodiscard]] bool hasPathTo (int v) const {
134
- if (!validVertex (v)) {
165
+ // whether a path from start to v exists
166
+ [[nodiscard]] bool hasPathTo (const int v) const {
167
+ if (!internal::validIndex (distTo, v)) {
135
168
return false ;
136
169
} else {
137
- return marked [v];
170
+ return distTo [v] != - 1 ;
138
171
}
139
172
}
140
173
174
+ // the saved path from start to v or an empty result
141
175
[[nodiscard]] std::vector<int > pathTo (const int v) const {
142
176
if (!hasPathTo (v)) {
143
177
return {};
@@ -155,98 +189,82 @@ namespace graph {
155
189
return path;
156
190
}
157
191
192
+ [[nodiscard]] int distanceTo (const int v) const {
193
+ if (!internal::validIndex (distTo, v)) {
194
+ return -1 ;
195
+ } else {
196
+ return distTo[v];
197
+ }
198
+ }
158
199
private:
159
200
const int start; // from which vertex the search started
160
- std::deque<bool > marked; // whether a vertex was already visited
161
- std::vector<int > edgeTo; // from which vertex another vertex was visited from the first time
162
-
163
- void dfs (const Graph& g, const int v) { // NOLINT (recursion)
164
- marked[v] = true ; // mark as visited
165
- for (const int other : g.adj (v)) {
166
- if (!marked[other]) {
167
- dfs (g, other);
168
- edgeTo[other] = v; // save that we got to other from v
201
+ const std::vector<int > distTo; // number of steps from start to vertex
202
+ const std::vector<int > edgeTo; // from which vertex another vertex was visited from the first time
203
+ };
204
+
205
+ namespace depth_first_search {
206
+ namespace internal {
207
+ // Recursive function for depth first search to all vertices from v
208
+ void fromVertexToAllRec (const Graph& g, const int v, // NOLINT
209
+ std::vector<int >& distTo, std::vector<int >& edgeTo) {
210
+ for (const int other : g.adj (v)) {
211
+ if (distTo[other] == -1 ) {
212
+ // not yet visited
213
+ distTo[other] = distTo[v]+1 ;
214
+ edgeTo[other] = v; // save that we got to other from v
215
+ fromVertexToAllRec (g, other, distTo, edgeTo);
216
+ }
169
217
}
170
218
}
171
219
}
172
220
173
- [[nodiscard]] bool validVertex (const int v) const {
174
- if (v < 0 || static_cast <size_t >(v) >= marked.size ()) {
175
- return false ;
176
- } else {
177
- return true ;
178
- }
221
+ // Depth-first search from vertex start to all reachable vertices
222
+ PathsFromVertexResult fromVertexToAll (const Graph &g, const int start) {
223
+ if (!g.vertexValid (start)) throw std::invalid_argument (" Invalid start vertex" );
224
+
225
+ std::vector<int > distTo (g.V (), -1 ); // number of steps from start to vertex
226
+ std::vector<int > edgeTo (g.V (), -1 ); // from which vertex another vertex was visited from the first time
227
+
228
+ distTo[start] = 0 ;
229
+ // start recursive depth-first start from start
230
+ internal::fromVertexToAllRec (g, start, distTo, edgeTo);
231
+
232
+ // return queryable results
233
+ return PathsFromVertexResult (start, std::move (distTo), std::move (edgeTo));
179
234
}
180
- };
235
+ } // depth_first_search
236
+
237
+ namespace breadth_first_search {
238
+ // Breadth-first search from vertex start to all reachable vertices
239
+ PathsFromVertexResult fromVertexToAll (const Graph &g, const int start) {
240
+ if (start < 0 || start >= g.V ()) throw std::invalid_argument (" Invalid start vertex" );
241
+
242
+ std::vector<int > distTo (g.V (), -1 ); // number of steps from start to vertex
243
+ std::vector<int > edgeTo (g.V (), -1 ); // from which vertex another vertex was visited from the first time
181
244
182
- // Breadth First Search for paths starting at a specific vertex
183
- class PathsFromBFS {
184
- public:
185
- explicit PathsFromBFS (const Graph &g, const int _start) :
186
- start {_start}, distTo(g.V(), -1 ), edgeTo(g.V(), -1 ) {
187
245
std::queue<int > queue{};
188
246
queue.push (start);
189
247
distTo[start] = 0 ;
190
- while (!queue.empty ()) {
248
+ while (!queue.empty ()) {
249
+ // remove vertex from queue
191
250
const int v = queue.front ();
192
251
queue.pop ();
193
- for (const int other : g.adj (v)) {
194
- if (distTo[other] == -1 ) {
252
+ // add all yet unvisited adjacent vertices to queue and mark them
253
+ for (const int other : g.adj (v)) {
254
+ if (distTo[other] == -1 ) {
195
255
// yet unvisited
196
256
queue.push (other);
197
257
edgeTo[other] = v;
198
- distTo[other] = distTo[v]+ 1 ;
258
+ distTo[other] = distTo[v] + 1 ;
199
259
}
200
260
}
201
261
}
202
- }
203
-
204
- [[nodiscard]] bool hasPathTo (int v) const {
205
- if (!validVertex (v)) {
206
- return false ;
207
- } else {
208
- return distTo[v] != -1 ;
209
- }
210
- }
211
-
212
- [[nodiscard]] int distanceTo (int v) const {
213
- if (!validVertex (v)) {
214
- return -1 ;
215
- } else {
216
- return distTo[v];
217
- }
218
- }
219
-
220
- [[nodiscard]] std::vector<int > pathTo (const int v) const {
221
- if (!hasPathTo (v)) {
222
- return {};
223
- }
224
-
225
- // build path in the reverse direction
226
- std::vector<int > path{v};
227
- int prev = edgeTo[v];
228
- while (prev != -1 ) {
229
- path.push_back (prev);
230
- prev = edgeTo[prev];
231
- }
232
262
233
- std::reverse (path. begin (), path. end ());
234
- return path ;
263
+ // return queryable results
264
+ return PathsFromVertexResult (start, std::move (distTo), std::move (edgeTo)) ;
235
265
}
236
266
237
- private:
238
- const int start; // from which vertex the search started
239
- std::vector<int > distTo; // number of steps from start to vertex
240
- std::vector<int > edgeTo; // from which vertex another vertex was visited from the first time
241
-
242
- [[nodiscard]] bool validVertex (const int v) const {
243
- if (v < 0 || static_cast <size_t >(v) >= distTo.size ()) {
244
- return false ;
245
- } else {
246
- return true ;
247
- }
248
- }
249
- };
267
+ }
250
268
251
269
} // namespace graph
252
270
0 commit comments