Graph Cpp
Helper Graph class for C++ with CMake support
Loading...
Searching...
No Matches
graph.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "utils.hpp"
4
5// MARK: Concepts
6
7template <typename vert_t>
9 std::is_arithmetic_v<vert_t> || std::is_same_v<vert_t, std::string>;
10
11template <typename weight_t>
12concept AllowedWeightType = std::is_arithmetic_v<weight_t>;
13
14// MARK: VertFromTuple
15
16template <AllowedVertType vert_t, AllowedWeightType weight_t>
17inline vert_t StartVertFromTuple(
18 const std::tuple<vert_t, vert_t, weight_t>& edge) {
19 return std::get<0>(edge);
20}
21
22template <AllowedVertType vert_t, AllowedWeightType weight_t>
23inline vert_t EndVertFromTuple(
24 const std::tuple<vert_t, vert_t, weight_t>& edge) {
25 return std::get<1>(edge);
26}
27
28template <AllowedVertType vert_t, AllowedWeightType weight_t>
29inline weight_t WeightFromTuple(
30 const std::tuple<vert_t, vert_t, weight_t>& edge) {
31 return std::get<2>(edge);
32}
33
34// MARK: Graph
35
36/**
37 * @brief Класс графа (может быть взвешенным и ориентированным).
38 *
39 * @tparam vert_t: тип вершин.
40 * @tparam weight_t: тип весов.
41 */
42template <AllowedVertType vert_t = std::string,
43 AllowedWeightType weight_t = size_t>
44class Graph {
45 public:
46 /// @brief Инициализирует новый экземпляр Graph.
47 Graph() : edges_(), verts_() {}
48
49 /**
50 * @brief Копирующий конструктор.
51 *
52 * @param other: граф, который нужно скопировать.
53 */
54 Graph(const Graph& other) = default;
55
56 /**
57 * @brief Оператор копирующего присваивания.
58 *
59 * @param other: граф, значения которого нужно присвоить.
60 *
61 * @return `Graph&`: ссылка на текущий объект.
62 */
63 Graph& operator=(const Graph& other) = default;
64
65 /**
66 * @brief Перемещающий конструктор. Перемещает ресурсы из другого графа.
67 *
68 * @param other: граф, ресурсы которого нужно переместить.
69 */
70 Graph(Graph&& other) noexcept
71 : edges_(std::move(other.edges_)),
72 verts_(std::move(other.verts_)),
73 is_direct_{other.is_direct_} {}
74
75 /**
76 * @brief Оператор перемещающего присваивания. Перемещает ресурсы из другого.
77 * графа.
78 *
79 * @param other: граф, ресурсы которого нужно переместить.
80 *
81 * @return `Graph&`: ссылка на текущий объект.
82 */
83 Graph& operator=(Graph&& other) noexcept {
84 if (this != &other) {
85 edges_ = std::move(other.edges_);
86 verts_ = std::move(other.verts_);
87 is_direct_ = other.is_direct_;
88 }
89
90 return *this;
91 }
92
93 /**
94 * @brief Создает новый экземпляр Graph по ребрам,
95 * представленными вектором std::pair (НЕВЗВЕШЕННЫЙ).
96 *
97 * @param edges_pairs: ребра графа.
98 *
99 * @return `Graph`: новый экземпляр Graph.
100 */
102 const std::vector<std::pair<vert_t, vert_t>>& edges_pairs) {
103 if (edges_pairs.empty()) return Graph();
104
105 std::vector<Edge> edges{};
106 edges.reserve(edges_pairs.size());
107
108 for (const auto& edge : edges_pairs) edges.push_back(edge);
109
110 return Graph(edges);
111 }
112
113 /**
114 * @brief Создает новый экземпляр Graph по ребрам,
115 * представленными вектором std::pair и weight_t (ВЗВЕШЕННЫЙ).
116 *
117 * @param edges_pairs: ребра графа.
118 * @param weights: веса ребер.
119 *
120 * @return `Graph`: новый экземпляр Graph.
121 *
122 * @throw `std::invalid_argument("GraphWeighted: the sizes of the edges and
123 * weights vectors do not match.")`.
124 */
126 const std::vector<std::pair<vert_t, vert_t>>& edges_pairs,
127 const std::vector<weight_t>& weights) {
128 if (edges_pairs.empty() && weights.empty()) return Graph();
129
130 std::vector<Edge> edges{};
131 edges.reserve(edges_pairs.size());
132
133 if (edges_pairs.size() != weights.size())
134 throw std::invalid_argument(
135 "GraphWeighted: the sizes of the edges and weights vectors do not "
136 "match.");
137
138 for (size_t i = 0; i < weights.size(); i++)
139 edges.push_back(Edge(edges_pairs[i], weights[i]));
140
141 return Graph(edges);
142 }
143
144 /**
145 * @brief Создает новый экземпляр Graph по ребрам.
146 * представленными вектором std::tuple (ВЗВЕШЕННЫЙ).
147 *
148 * @param edges_tuples: ребра графа.
149 *
150 * @return `Graph`: новый экземпляр Graph.
151 */
153 const std::vector<std::tuple<vert_t, vert_t, weight_t>>& edges_tuples) {
154 if (edges_tuples.empty()) return Graph();
155
156 std::vector<Edge> edges;
157
158 for (const auto& [start, end, weight] : edges_tuples)
159 edges.emplace_back(start, end, weight);
160
161 return Graph(edges);
162 }
163
164 /**
165 * @brief Создает новый экземпляр Graph по ребрам.
166 * представленными вектором std::string (НЕВЗВЕШЕННЫЙ).
167 *
168 * @param edges_strs: ребра графа.
169 *
170 * @return `Graph`: новый экземпляр Graph.
171 */
172 static Graph GraphFromStrs(const std::vector<std::string>& edges_strs) {
173 if (edges_strs.empty()) return Graph();
174
175 std::vector<Graph<vert_t, weight_t>::Edge> edges;
176
177 for (const auto& edge_str : edges_strs) {
178 vert_t start_vert, end_vert;
179 std::tie(start_vert, end_vert) = ParseEdgeString_(edge_str);
180
181 edges.emplace_back(start_vert, end_vert);
182 }
183
184 return Graph(edges);
185 }
186
187 /**
188 * @brief Создает новый экземпляр Graph по ребрам.
189 * представленными словарем из std::string и weight_t (ВЗВЕШЕННЫЙ).
190 *
191 * @param edges_dict: ребра графа.
192 *
193 * @return `Graph`: новый экземпляр Graph.
194 */
196 const std::unordered_map<std::string, weight_t>& edges_dict) {
197 if (edges_dict.empty()) return Graph();
198
199 std::vector<Graph<vert_t, weight_t>::Edge> edges;
200
201 for (const auto& [edge_str, weight] : edges_dict) {
202 vert_t start_vert, end_vert;
203 std::tie(start_vert, end_vert) = ParseEdgeString_(edge_str);
204
205 edges.emplace_back(start_vert, end_vert, weight);
206 }
207
208 return Graph(edges);
209 }
210
211 /**
212 * @brief Создает новый экземпляр Graph по матрице смежности.
213 *
214 * @param adj_matrix: матрица смежности.
215 * @param is_weighted: взвешен ли граф.
216 *
217 * @return `Graph`: новый экземпляр Graph.
218 *
219 * @throw `std::invalid_argument("GraphFromAdjMatrix: AdjacencyMatrix is not
220 * squared.")`.
221 * @throw `std::invalid_argument("GraphFromAdjMatrix: AdjacencyMatrix is not
222 * squared [row problem].")`.
223 * @throw `std::logic_error("GraphFromAdjMatrix: this method (constructor) is
224 * deleted for std::string.")`.
225 */
227 const std::vector<std::vector<weight_t>>& adj_matrix,
228 bool is_weighted = false) {
229 if constexpr (std::is_arithmetic_v<vert_t>) {
230 if (adj_matrix.empty()) return Graph();
231
232 std::vector<Edge> edges{};
233
234 if (adj_matrix.size() != adj_matrix[0].size())
235 throw std::invalid_argument(
236 "GraphFromAdjMatrix: AdjacencyMatrix is not squared.");
237
238 for (size_t row = 0; row < adj_matrix.size(); row++) {
239 if (row != 0)
240 if (adj_matrix[row].size() != adj_matrix[row - 1].size())
241 throw std::invalid_argument(
242 "GraphFromAdjMatrix: AdjacencyMatrix is not squared [row "
243 "problem].");
244
245 for (size_t col = 0; col < adj_matrix[row].size(); col++) {
246 if (adj_matrix[row][col] == 0) continue;
247
248 if (is_weighted)
249 edges.push_back(Edge(col, row, adj_matrix[col][row]));
250 else
251 edges.push_back(Edge(col, row));
252 }
253 }
254
255 return Graph(edges);
256
257 } else if constexpr (std::is_same_v<vert_t, std::string>)
258 throw std::logic_error(
259 "GraphFromAdjMatrix: this method (constructor) "
260 "is deleted for std::string.");
261 }
262
263 /**
264 * @brief Создает новый экземпляр Graph.
265 * по списку смежности (НЕВЗВЕШЕННЫЙ).
266 *
267 * @param adj_list: список смежности.
268 *
269 * @return `Graph`: новый экземпляр Graph.
270 *
271 * @throw std::logic_error("GraphFromAdjList: this method (constructor) is
272 * deleted for std::string.").
273 */
275 const std::vector<std::vector<vert_t>>& adj_list) {
276 if constexpr (std::is_arithmetic_v<vert_t>) {
277 if (adj_list.empty()) return Graph();
278
279 std::vector<Edge> edges{};
280
281 for (size_t row = 0; row < adj_list.size(); row++)
282 for (size_t col = 0; col < adj_list[row].size(); col++)
283 edges.push_back(Edge(row, adj_list[row][col]));
284
285 return Graph(edges);
286
287 } else if constexpr (std::is_same_v<vert_t, std::string>)
288 throw std::logic_error(
289 "GraphFromAdjList: this method (constructor) "
290 "is deleted for std::string.");
291 }
292
293 /**
294 * @brief Создает новый экземпляр Graph.
295 * по списку смежности с указанием вершины-ключа (НЕВЗВЕШЕННЫЙ).
296 *
297 * @param adj_list_dict: список смежности с указанием вершины-ключа.
298 *
299 * @return `Graph`: новый экземпляр Graph.
300 */
302 const std::unordered_map<vert_t, std::vector<vert_t>>& adj_list_dict) {
303 if (adj_list_dict.empty()) return Graph();
304
305 std::vector<Edge> edges{};
306
307 for (const auto& vert_str_pair : adj_list_dict) {
308 auto vert = vert_str_pair.first;
309 for (const auto& vert_2 : vert_str_pair.second)
310 edges.push_back(Edge(vert, vert_2));
311 }
312
313 return Graph(edges);
314 }
315
316 /// @brief Проверяет, взвешен ли граф.
317 bool IsWeighted() const { return is_weighted_; }
318
319 /// @return `size_t`: кол-во вершин.
320 size_t VertsAmount() const { return verts_.size(); }
321
322 /// @return `vert_t`: кол-во ребер.
323 size_t EdgesAmount() const { return edges_.size(); }
324
325 /// @return `const std::vector<vert_t>&`: вершины.
326 const std::vector<vert_t>& Verts() const { return verts_; }
327
328 /// @return `std::vector<std::tuple<vert_t, vert_t, weight_t>>`: ребра.
329 std::vector<std::tuple<vert_t, vert_t, weight_t>> Edges() const {
330 if (edges_.empty()) return {};
331
332 std::vector<std::tuple<vert_t, vert_t, weight_t>> edges_tuples(
333 edges_.size());
334 std::transform(edges_.begin(), edges_.end(), edges_tuples.begin(),
335 [](const Edge& edge) {
336 return std::make_tuple(edge.StartVert(), edge.EndVert(),
337 edge.Weight());
338 });
339
340 return edges_tuples;
341 }
342
343 /**
344 * @brief Выводит в поток список вершин.
345 *
346 * @param os: входной поток.
347 *
348 * @return std::ostream&: выходной поток.
349 */
350 std::ostream& PrintVerts(std::ostream& os = std::cout) const {
351 os << Verts();
352 return os;
353 }
354
355 /**
356 * @brief Выводит в поток список ребер.
357 *
358 * @param os: входной поток.
359 *
360 * @return `std::ostream&`: выходной поток.
361 */
362 std::ostream& PrintEdges(std::ostream& os = std::cout) const {
363 os << edges_;
364 return os;
365 }
366
367 /**
368 * @brief Выводит в поток список смежности.
369 *
370 * @param os: входной поток.
371 *
372 * @return `std::ostream&`: выходной поток.
373 */
374 std::ostream& PrintAdjList(std::ostream& os = std::cout) const {
375 for (const auto& vert : Verts()) {
376 os << vert << ": ";
377
378 for (const auto& neighbor : edges_) {
379 if (neighbor.StartVert() == vert) os << neighbor.EndVert() << "; ";
380 if (!IsDirected())
381 if (neighbor.EndVert() == vert) os << neighbor.StartVert() << "; ";
382 }
383
384 os << "\n";
385 }
386
387 return os;
388 }
389
390 /// @brief Делает граф ненаправленным (удаляет лишние ребра).
392 std::unordered_set<size_t> seen_edges;
393 std::vector<Edge> unique_edges;
394 unique_edges.reserve(EdgesAmount());
395
396 for (size_t i = 0; i < EdgesAmount(); i++) {
397 if (seen_edges.count(i) != 0) continue;
398
399 for (size_t j = i + 1; j < EdgesAmount(); j++)
400 if (edges_[i].StartVert() == edges_[j].EndVert() &&
401 edges_[j].StartVert() == edges_[i].EndVert()) {
402 seen_edges.insert(j);
403 break;
404 }
405
406 unique_edges.push_back(edges_[i]);
407 }
408
409 edges_ = std::move(unique_edges);
410 is_direct_ = false;
411 }
412
413 /// @brief Делает граф направленным (ничего).
414 void MakeDirected() { is_direct_ = true; }
415
416 /// @brief Проверяет, направлен ли граф.
417 bool IsDirected() const { return is_direct_; }
418
419 /**
420 * @return `std::vector<std::vector<vert_t>>`: список смежности.
421 * @throw `std::logic_error("GetAdjListWithoutKeys: this method is deleted
422 * for std::string.")`.
423 */
424 std::vector<std::vector<vert_t>> GetAdjListWithoutKeys() const {
425 if constexpr (std::is_arithmetic_v<vert_t>) {
426 std::vector<std::vector<vert_t>> adj_list(
427 *std::max_element(Verts().begin(), Verts().end()) + 1);
428
429 for (const auto& edge : edges_) {
430 adj_list[edge.StartVert()].push_back(edge.EndVert());
431 if (!IsDirected()) adj_list[edge.EndVert()].push_back(edge.StartVert());
432 }
433
434 return adj_list;
435 }
436
437 else if constexpr (std::is_same_v<vert_t, std::string>)
438 throw std::logic_error(
439 "GetAdjListWithoutKeys: this method is deleted for std::string.");
440 }
441
442 /// @return `std::unordered_map<vert_t, std::vector<vert_t>>`: список
443 /// смежности с указанием вершины-ключа.
444 std::unordered_map<vert_t, std::vector<vert_t>> GetAdjList() const {
445 auto adj_list_dict = std::unordered_map<vert_t, std::vector<vert_t>>();
446
447 for (const auto& edge : edges_) {
448 adj_list_dict[edge.StartVert()].push_back(edge.EndVert());
449 if (!IsDirected())
450 adj_list_dict[edge.EndVert()].push_back(edge.StartVert());
451 }
452
453 return adj_list_dict;
454 }
455
456 /**
457 * @return `std::vector<std::vector<vert_t>>`: матрица смежности.
458 * @throw `std::logic_error("GetAdjMatrix: this method is deleted for
459 * std::string.")`.
460 */
461 std::vector<std::vector<weight_t>> GetAdjMatrix() const {
462 if constexpr (std::is_arithmetic_v<vert_t>) {
463 std::vector<std::vector<weight_t>> adj_matrix(
464 VertsAmount(), std::vector<weight_t>(VertsAmount(), 0));
465
466 for (const auto& edge : edges_)
467 if (edge.IsWeighted()) {
468 adj_matrix[edge.StartVert()][edge.EndVert()] = edge.Weight();
469 if (!IsDirected())
470 adj_matrix[edge.EndVert()][edge.StartVert()] = edge.Weight();
471 } else {
472 adj_matrix[edge.StartVert()][edge.EndVert()] = 1;
473 if (!IsDirected()) adj_matrix[edge.EndVert()][edge.StartVert()] = 1;
474 }
475
476 return adj_matrix;
477 }
478
479 else if constexpr (std::is_same_v<vert_t, std::string>)
480 throw std::logic_error(
481 "GetAdjMatrix: this method is deleted for std::string.");
482 }
483
484 /**
485 * @brief Проверяет, содержится ли вершина в графе.
486 *
487 * @param vert: вершина.
488 *
489 * @return `true`: содержится.
490 * @return `false`: не содержится.
491 */
492 bool ContainsVert(const vert_t& vert) const {
493 return std::find(Verts().begin(), Verts().end(), vert) != Verts().end();
494 }
495
496 /**
497 * @brief Проверяет, содержится ли ребро в графе (ВЗВЕШЕННЫЙ).
498 *
499 * @param edge: ребро.
500 *
501 * @return `true`: содержится.
502 * @return `false`: не содержится.
503 *
504 * @throw `std::logic_error("ContainsEdge: graph is not weighted.")`.
505 */
506 bool ContainsEdge(const std::tuple<vert_t, vert_t, weight_t>& edge) const {
507 if (!IsWeighted())
508 throw std::logic_error("ContainsEdge: graph is not weighted.");
509
510 return GetEdgeIter_(edge) != edges_.end();
511 }
512
513 /**
514 * @brief Проверяет, содержится ли ребро в графе (НЕВЗВЕШЕННЫЙ).
515 *
516 * @param edge: ребро.
517 *
518 * @return `true`: содержится.
519 * @return `false`: не содержится.
520 */
521 bool ContainsEdge(const std::pair<vert_t, vert_t>& edge) const {
522 return GetEdgeIter_(edge) != edges_.end();
523 }
524
525 /**
526 * @brief Находит вес ребра в взвешенном графе.
527 *
528 * @param edge: ребро.
529 *
530 * @return `weight_t`: вес.
531 *
532 * @throw `std::logic_error("GetEdgeWeight: graph is not weighted.")`.
533 * @throw `std::invalid_argument("GetEdgeWeight: there is no such edge:")`.
534 */
535 weight_t GetEdgeWeight(const std::pair<vert_t, vert_t>& edge) const {
536 if (!IsWeighted())
537 throw std::logic_error("GetEdgeWeight: graph is not weighted.");
538
539 auto it = GetEdgeIter_(edge);
540
541 if (it == edges_.end())
542 throw std::invalid_argument("GetEdgeWeight: there is no such edge: " +
543 Edge(edge).Name());
544
545 return it->Weight();
546 }
547
548 /**
549 * @brief Меняет вес ребра в взвешенном графе.
550 *
551 * @param edge: ребро.
552 * @param new_weight: вес.
553 *
554 * @throw `std::logic_error("SetEdgeWeight: graph is not weighted.")`.
555 * @throw `std::invalid_argument("SetEdgeWeight: there is no such edge:")`.
556 */
557 void SetEdgeWeight(const std::pair<vert_t, vert_t>& edge,
558 weight_t new_weight) {
559 if (!IsWeighted())
560 throw std::logic_error("SetEdgeWeight: graph is not weighted.");
561
562 auto it = GetEdgeIter_(edge);
563
564 if (it == edges_.end())
565 throw std::invalid_argument("SetEdgeWeight: there is no such edge: " +
566 Edge(edge).Name());
567
568 it->SetWeight(new_weight);
569 }
570
571 void AddVert(const vert_t& vert) {
572 if (!Contains(verts_, vert)) verts_.push_back(vert);
573 }
574
575 /// @warning `"AddEdge: weighted graph must consist of weighted edges.`
576 void AddEdge(const std::tuple<vert_t, vert_t, weight_t>& edge_tuple,
577 bool ignore_warning = false) {
578 if (WeightFromTuple(edge_tuple) == 0)
579 AddEdge({StartVertFromTuple(edge_tuple), EndVertFromTuple(edge_tuple)},
580 ignore_warning);
581 else
582 AddEdge_(edge_tuple);
583 }
584
585 /// @warning `"AddEdge: weighted graph must consist of weighted edges.`
586 void AddEdge(const std::pair<vert_t, vert_t>& edge_pair,
587 bool ignore_warning = false) {
588 if (IsWeighted() && !ignore_warning)
589 std::cerr << "Warning! AddEdge: weighted graph should consist of "
590 "weighted edges."
591 << std::endl;
592
593 AddEdge_(Edge(edge_pair.first, edge_pair.second, static_cast<weight_t>(0)));
594 }
595
596 /// @throw `std::invalid_argument("RemoveVert: there is no such vert:")`.
597 void RemoveVert(const vert_t& vert) {
598 if constexpr (std::is_arithmetic_v<vert_t>) {
599 if (!Contains(Verts(), vert))
600 throw std::invalid_argument(
601 "RemoveVert: there is no such vert in graph: " +
602 std::to_string(vert));
603 }
604
605 else if constexpr (std::is_same_v<vert_t, std::string>)
606 if (!Contains(Verts(), vert))
607 throw std::invalid_argument(
608 "RemoveVert: there is no such vert in graph: " + vert);
609
610 verts_.erase(std::remove(verts_.begin(), verts_.end(), vert), verts_.end());
611
612 edges_.erase(std::remove_if(edges_.begin(), edges_.end(),
613 [vert](const Edge& edge) {
614 return edge.StartVert() == vert ||
615 edge.EndVert() == vert;
616 }),
617 edges_.end());
618 }
619
620 /// @throw `std::invalid_argument("RemoveEdge: there is no such edge:")`.
621 void RemoveEdge(const std::pair<vert_t, vert_t>& edge_pair) {
622 if (!ContainsEdge(edge_pair))
623 throw std::invalid_argument(
624 "RemoveEdge: there is no such edge in graph: " +
625 Edge(edge_pair).Name());
626
627 edges_.erase(std::remove_if(edges_.begin(), edges_.end(),
628 [&edge_pair, this](const Edge& e) {
629 return (e == Edge(edge_pair)) ||
630 (!IsDirected() &&
631 Edge(e.EndVert(), e.StartVert()) ==
632 Edge(edge_pair));
633 }),
634 edges_.end());
635 }
636
637 /// @throw `std::invalid_argument("RemoveEdge: there is no such edge:")`.
638 void RemoveEdge(const std::tuple<vert_t, vert_t, weight_t>& edge_tuple) {
639 if (!ContainsEdge(edge_tuple))
640 throw std::invalid_argument(
641 "RemoveEdge: there is no such edge in graph: " +
642 Edge(edge_tuple).Name());
643
644 edges_.erase(
645 std::remove_if(
646 edges_.begin(), edges_.end(),
647 [&edge_tuple, this](const Edge& e) {
648 return (e == Edge(edge_tuple)) ||
649 (!IsDirected() &&
650 e == Edge(std::make_tuple(EndVertFromTuple(edge_tuple),
651 StartVertFromTuple(edge_tuple),
652 WeightFromTuple(edge_tuple))));
653 }),
654 edges_.end());
655 }
656
657 private:
658 class Edge {
659 public:
660 Edge() = delete;
661
662 Edge(const vert_t start_vert, const vert_t& end_vert)
663 : start_vert_{start_vert}, end_vert_{end_vert} {}
664
665 Edge(const vert_t& start_vert, vert_t end_vert, weight_t weight)
666 : start_vert_{start_vert}, end_vert_{end_vert}, weight_{weight} {}
667
668 Edge(const std::pair<vert_t, vert_t>& edge_pair)
669 : start_vert_{edge_pair.first}, end_vert_{edge_pair.second} {}
670
671 Edge(const std::pair<vert_t, vert_t>& edge_pair, weight_t weight)
672 : start_vert_{edge_pair.first},
673 end_vert_{edge_pair.second},
674 weight_{weight} {}
675
676 Edge(const std::tuple<vert_t, vert_t, weight_t>& edge_tuple)
677 : start_vert_{StartVertFromTuple(edge_tuple)},
678 end_vert_{EndVertFromTuple(edge_tuple)},
679 weight_{WeightFromTuple(edge_tuple)} {}
680
681 bool IsWeighted() const { return weight_ != 0; }
682
683 vert_t StartVert() const { return start_vert_; }
684 vert_t EndVert() const { return end_vert_; }
685
686 weight_t Weight() const { return weight_; }
687
688 void SetWeight(weight_t new_weight) { weight_ = new_weight; }
689
690 bool operator==(const Edge& rhs) const {
691 if (StartVert() != rhs.StartVert() || EndVert() != rhs.EndVert())
692 return false;
693
694 if (IsWeighted() && rhs.IsWeighted()) return Weight() == rhs.Weight();
695
696 return true;
697 }
698
699 bool operator!=(const Edge& rhs) const { return !(*this == rhs); }
700
701 auto operator<=>(const Edge& rhs) const { return weight_ <=> rhs.Weight(); }
702
703 const std::string& Name() const {
704 static std::string name;
705
706 if constexpr (std::is_arithmetic_v<vert_t>) {
707 if (IsWeighted())
708 name = "[" + std::to_string(StartVert()) + "->" +
709 std::to_string(EndVert()) +
710 ", w: " + std::to_string(Weight()) + "]";
711 else
712 name = "[" + std::to_string(StartVert()) + "->" +
713 std::to_string(EndVert()) + "]";
714
715 // example: "[4->5]"
716
717 } else if constexpr (std::is_same_v<vert_t, std::string>) {
718 if (IsWeighted())
719 name = "['" + StartVert() + "'->'" + EndVert() +
720 "', w: " + std::to_string(Weight()) + "]";
721 else
722 name = "['" + StartVert() + "'->'" + EndVert() + "']";
723
724 // example: "["Paris"->"Berlin"]"
725 }
726
727 return name;
728 }
729
730 private:
734 };
735
736 std::vector<Edge> edges_;
737 std::vector<vert_t> verts_;
738
739 bool is_direct_ = true;
740 bool is_weighted_ = false;
741
742 public:
743 friend std::ostream& operator<<(std::ostream& os,
744 const Graph<vert_t, weight_t>::Edge& edge) {
745 os << edge.Name();
746 return os;
747 }
748
749 private:
750 Graph(const std::vector<Edge>& edges) {
751 if (edges.empty()) return;
752
753 for (const auto& edge : edges) {
754 if (edge.IsWeighted()) is_weighted_ = true;
755 AddEdge_(edge);
756 }
757
758 if constexpr (std::is_arithmetic_v<vert_t>) {
759 // кол-во вершин = максимальная вершина среди ребер, т.е. в этом случае
760 // происходит заполнение вершин до наибольшей из них в списке ребер
761 vert_t max_vert = edges[0].StartVert();
762
763 for (const auto& edge : edges_) {
764 max_vert = std::max(max_vert, edge.StartVert());
765 max_vert = std::max(max_vert, edge.EndVert());
766 }
767
768 verts_.resize(max_vert + 1);
769 std::iota(verts_.begin(), verts_.end(), 0);
770
771 } else if constexpr (std::is_same_v<vert_t, std::string>)
772 for (const auto& edge : edges_) {
773 if (!Contains(Verts(), edge.StartVert()))
774 verts_.push_back(edge.StartVert());
775
776 if (!Contains(Verts(), edge.EndVert()))
777 verts_.push_back(edge.EndVert());
778 }
779 }
780
781 void AddEdge_(const Edge& edge) {
782 AddVert(edge.StartVert());
783 AddVert(edge.EndVert());
784
785 if (!Contains(edges_, edge)) edges_.emplace_back(edge);
786
787 if (edge.Weight() != 0) is_weighted_ = true;
788 }
789
790 static std::pair<vert_t, vert_t> ParseEdgeString_(
791 const std::string& edge_str) {
792 const size_t pos = edge_str.find("->");
793 vert_t start_vert;
794 vert_t end_vert;
795
796 if (pos == std::string::npos)
797 throw std::invalid_argument("EdgeString: invalid edge string format: " +
798 edge_str);
799
800 if constexpr (std::is_arithmetic_v<vert_t>) {
801 try {
802 start_vert = std::stoul(edge_str.substr(0, pos));
803 end_vert = std::stoul(edge_str.substr(pos + 2));
804 }
805
806 catch (const std::exception&) {
807 throw std::invalid_argument(
808 "EdgeString: invalid edge string format "
809 "(vertices should be numbers): " +
810 edge_str);
811 }
812 }
813
814 else if constexpr (std::is_same_v<vert_t, std::string>) {
815 start_vert = edge_str.substr(0, pos);
816 end_vert = edge_str.substr(pos + 2);
817 }
818
819 return {start_vert, end_vert};
820 }
821
822 auto GetEdgeIter_(const std::pair<vert_t, vert_t>& edge) const {
823 auto [start_vert, end_vert] = edge;
824
825 return std::find_if(
826 edges_.begin(), edges_.end(),
827 [start_vert, end_vert, this](const auto& e) {
828 return (e.StartVert() == start_vert && e.EndVert() == end_vert) ||
829 (!IsDirected() && e.StartVert() == end_vert &&
830 e.EndVert() == start_vert);
831 });
832 }
833
834 auto GetEdgeIter_(const std::pair<vert_t, vert_t>& edge) {
835 auto [start_vert, end_vert] = edge;
836
837 return std::find_if(
838 edges_.begin(), edges_.end(),
839 [start_vert, end_vert, this](const auto& e) {
840 return (e.StartVert() == start_vert && e.EndVert() == end_vert) ||
841 (!IsDirected() && e.StartVert() == end_vert &&
842 e.EndVert() == start_vert);
843 });
844 }
845
846 auto GetEdgeIter_(const std::tuple<vert_t, vert_t, weight_t>& edge) const {
847 auto [start_vert, end_vert, weight] = edge;
848
849 return std::find_if(
850 edges_.begin(), edges_.end(),
851 [start_vert, end_vert, weight, this](const auto& e) {
852 return (e.StartVert() == start_vert && e.EndVert() == end_vert &&
853 e.Weight() == weight) ||
854 (!IsDirected() && e.StartVert() == end_vert &&
855 e.EndVert() == start_vert && e.Weight() == weight);
856 });
857 }
858};
859
860// MARK: operator<<
861
862template <AllowedVertType vert_t, AllowedWeightType weight_t>
863inline std::ostream& operator<<(std::ostream& os,
864 const Graph<vert_t, weight_t>& graph) {
865 os << "Edges:\n ";
866 graph.PrintEdges(os);
867
868 os << "\n";
869
870 os << "Vertices:\n ";
871 graph.PrintVerts(os);
872 return os;
873}
Definition graph.hpp:658
Edge(const vert_t &start_vert, vert_t end_vert, weight_t weight)
Definition graph.hpp:665
bool operator==(const Edge &rhs) const
Definition graph.hpp:690
Edge(const vert_t start_vert, const vert_t &end_vert)
Definition graph.hpp:662
const std::string & Name() const
Definition graph.hpp:703
Edge(const std::pair< vert_t, vert_t > &edge_pair)
Definition graph.hpp:668
Edge(const std::pair< vert_t, vert_t > &edge_pair, weight_t weight)
Definition graph.hpp:671
bool IsWeighted() const
Definition graph.hpp:681
auto operator<=>(const Edge &rhs) const
Definition graph.hpp:701
void SetWeight(weight_t new_weight)
Definition graph.hpp:688
Edge(const std::tuple< vert_t, vert_t, weight_t > &edge_tuple)
Definition graph.hpp:676
vert_t start_vert_
Definition graph.hpp:731
bool operator!=(const Edge &rhs) const
Definition graph.hpp:699
weight_t Weight() const
Definition graph.hpp:686
vert_t end_vert_
Definition graph.hpp:732
vert_t StartVert() const
Definition graph.hpp:683
vert_t EndVert() const
Definition graph.hpp:684
weight_t weight_
Definition graph.hpp:733
Edge()=delete
Класс графа (может быть взвешенным и ориентированным).
Definition graph.hpp:44
void AddEdge_(const Edge &edge)
Definition graph.hpp:781
void SetEdgeWeight(const std::pair< vert_t, vert_t > &edge, weight_t new_weight)
Меняет вес ребра в взвешенном графе.
Definition graph.hpp:557
bool is_direct_
Definition graph.hpp:739
std::vector< std::tuple< vert_t, vert_t, weight_t > > Edges() const
Definition graph.hpp:329
weight_t GetEdgeWeight(const std::pair< vert_t, vert_t > &edge) const
Находит вес ребра в взвешенном графе.
Definition graph.hpp:535
std::ostream & PrintEdges(std::ostream &os=std::cout) const
Выводит в поток список ребер.
Definition graph.hpp:362
std::vector< std::vector< vert_t > > GetAdjListWithoutKeys() const
Definition graph.hpp:424
static Graph GraphWeighted(const std::vector< std::tuple< vert_t, vert_t, weight_t > > &edges_tuples)
Создает новый экземпляр Graph по ребрам. представленными вектором std::tuple (ВЗВЕШЕННЫЙ).
Definition graph.hpp:152
auto GetEdgeIter_(const std::pair< vert_t, vert_t > &edge)
Definition graph.hpp:834
bool ContainsEdge(const std::pair< vert_t, vert_t > &edge) const
Проверяет, содержится ли ребро в графе (НЕВЗВЕШЕННЫЙ).
Definition graph.hpp:521
static Graph GraphFromAdjList(const std::unordered_map< vert_t, std::vector< vert_t > > &adj_list_dict)
Создает новый экземпляр Graph. по списку смежности с указанием вершины-ключа (НЕВЗВЕШЕННЫЙ).
Definition graph.hpp:301
size_t VertsAmount() const
Definition graph.hpp:320
static Graph GraphFromAdjMatrix(const std::vector< std::vector< weight_t > > &adj_matrix, bool is_weighted=false)
Создает новый экземпляр Graph по матрице смежности.
Definition graph.hpp:226
void MakeUndirected()
Делает граф ненаправленным (удаляет лишние ребра).
Definition graph.hpp:391
bool ContainsEdge(const std::tuple< vert_t, vert_t, weight_t > &edge) const
Проверяет, содержится ли ребро в графе (ВЗВЕШЕННЫЙ).
Definition graph.hpp:506
const std::vector< vert_t > & Verts() const
Definition graph.hpp:326
void MakeDirected()
Делает граф направленным (ничего).
Definition graph.hpp:414
auto GetEdgeIter_(const std::pair< vert_t, vert_t > &edge) const
Definition graph.hpp:822
void RemoveEdge(const std::tuple< vert_t, vert_t, weight_t > &edge_tuple)
Definition graph.hpp:638
std::vector< std::vector< weight_t > > GetAdjMatrix() const
Definition graph.hpp:461
void AddEdge(const std::tuple< vert_t, vert_t, weight_t > &edge_tuple, bool ignore_warning=false)
Definition graph.hpp:576
bool is_weighted_
Definition graph.hpp:740
Graph(const std::vector< Edge > &edges)
Definition graph.hpp:750
static Graph GraphWeighted(const std::vector< std::pair< vert_t, vert_t > > &edges_pairs, const std::vector< weight_t > &weights)
Создает новый экземпляр Graph по ребрам, представленными вектором std::pair и weight_t (ВЗВЕШЕННЫЙ).
Definition graph.hpp:125
std::unordered_map< vert_t, std::vector< vert_t > > GetAdjList() const
Definition graph.hpp:444
Graph(Graph &&other) noexcept
Перемещающий конструктор. Перемещает ресурсы из другого графа.
Definition graph.hpp:70
static Graph GraphNonWeighted(const std::vector< std::pair< vert_t, vert_t > > &edges_pairs)
Создает новый экземпляр Graph по ребрам, представленными вектором std::pair (НЕВЗВЕШЕННЫЙ).
Definition graph.hpp:101
Graph(const Graph &other)=default
Копирующий конструктор.
std::ostream & PrintAdjList(std::ostream &os=std::cout) const
Выводит в поток список смежности.
Definition graph.hpp:374
void RemoveEdge(const std::pair< vert_t, vert_t > &edge_pair)
Definition graph.hpp:621
Graph & operator=(const Graph &other)=default
Оператор копирующего присваивания.
std::ostream & PrintVerts(std::ostream &os=std::cout) const
Выводит в поток список вершин.
Definition graph.hpp:350
bool IsDirected() const
Проверяет, направлен ли граф.
Definition graph.hpp:417
static Graph GraphFromStrs(const std::vector< std::string > &edges_strs)
Создает новый экземпляр Graph по ребрам. представленными вектором std::string (НЕВЗВЕШЕННЫЙ).
Definition graph.hpp:172
std::vector< Edge > edges_
Definition graph.hpp:736
auto GetEdgeIter_(const std::tuple< vert_t, vert_t, weight_t > &edge) const
Definition graph.hpp:846
Graph()
Инициализирует новый экземпляр Graph.
Definition graph.hpp:47
bool ContainsVert(const vert_t &vert) const
Проверяет, содержится ли вершина в графе.
Definition graph.hpp:492
static Graph GraphFromAdjList(const std::vector< std::vector< vert_t > > &adj_list)
Создает новый экземпляр Graph. по списку смежности (НЕВЗВЕШЕННЫЙ).
Definition graph.hpp:274
static Graph GraphFromMap(const std::unordered_map< std::string, weight_t > &edges_dict)
Создает новый экземпляр Graph по ребрам. представленными словарем из std::string и weight_t (ВЗВЕШЕНН...
Definition graph.hpp:195
static std::pair< vert_t, vert_t > ParseEdgeString_(const std::string &edge_str)
Definition graph.hpp:790
void AddEdge(const std::pair< vert_t, vert_t > &edge_pair, bool ignore_warning=false)
Definition graph.hpp:586
bool IsWeighted() const
Проверяет, взвешен ли граф.
Definition graph.hpp:317
void RemoveVert(const vert_t &vert)
Definition graph.hpp:597
std::vector< vert_t > verts_
Definition graph.hpp:737
size_t EdgesAmount() const
Definition graph.hpp:323
Graph & operator=(Graph &&other) noexcept
Оператор перемещающего присваивания. Перемещает ресурсы из другого. графа.
Definition graph.hpp:83
friend std::ostream & operator<<(std::ostream &os, const Graph< vert_t, weight_t >::Edge &edge)
Definition graph.hpp:743
void AddVert(const vert_t &vert)
Definition graph.hpp:571
Definition graph.hpp:8
Definition graph.hpp:12
std::ostream & operator<<(std::ostream &os, const Graph< vert_t, weight_t > &graph)
Definition graph.hpp:863
vert_t EndVertFromTuple(const std::tuple< vert_t, vert_t, weight_t > &edge)
Definition graph.hpp:23
weight_t WeightFromTuple(const std::tuple< vert_t, vert_t, weight_t > &edge)
Definition graph.hpp:29
vert_t StartVertFromTuple(const std::tuple< vert_t, vert_t, weight_t > &edge)
Definition graph.hpp:17
bool Contains(const std::vector< T > &vec, const T &value)
Проверяет наличие элемента в векторе.
Definition utils.hpp:193