Pangolin
Header-only C++20 plane computational geometry library
Loading...
Searching...
No Matches
graph.hpp
Go to the documentation of this file.
1#pragma once
2
7
9
10#include <algorithm>
11#include <array>
12#include <cassert>
13#include <concepts>
14#include <cstddef>
15#include <iterator>
16#include <optional>
17#include <queue>
18#include <ranges>
19#include <stack>
20#include <type_traits>
21#include <unordered_map>
22#include <unordered_set>
23#include <utility>
24#include <vector>
25
26namespace pgl {
27
37template <class Vertex>
38class Graph {
39 using AdjacencyMap = std::unordered_map<Vertex, std::unordered_set<Vertex>>;
40
41public:
43 using VertexType = Vertex;
44
46 using NeighborSet = std::unordered_set<Vertex>;
47
49 using EdgeType = std::array<Vertex, 2>;
50
57 class Iterator {
58 using BaseIterator = typename AdjacencyMap::const_iterator;
59
60 public:
61 using iterator_category = std::forward_iterator_tag;
62 using iterator_concept = std::forward_iterator_tag;
63 using difference_type = std::ptrdiff_t;
64 using value_type = Vertex;
65 using pointer = const Vertex*;
66 using reference = const Vertex&;
67
69 Iterator() = default;
70
73 return iterator_->first;
74 }
75
78 return &iterator_->first;
79 }
80
83 ++iterator_;
84 return *this;
85 }
86
89 Iterator previous = *this;
90 ++(*this);
91 return previous;
92 }
93
94 friend bool operator==(const Iterator&, const Iterator&) = default;
95
96 private:
97 friend class Graph;
98
99 explicit Iterator(BaseIterator iterator)
100 : iterator_(iterator) {}
101
102 BaseIterator iterator_;
103 };
104
116 using OuterIterator = typename AdjacencyMap::const_iterator;
117 using InnerIterator = typename NeighborSet::const_iterator;
118
119 public:
120 // Multi-pass, hence a forward iterator to the C++20 concepts, but the
121 // edge is built on dereference rather than stored, so the old category
122 // stays "input": only the concept admits a reference that is a value.
123 using iterator_category = std::input_iterator_tag;
124 using iterator_concept = std::forward_iterator_tag;
125 using difference_type = std::ptrdiff_t;
128
130 EdgeIterator() = default;
131
134 return EdgeType{outer_->first, *inner_};
135 }
136
139 ++inner_;
140 skipToEdge();
141 return *this;
142 }
143
146 EdgeIterator previous = *this;
147 ++(*this);
148 return previous;
149 }
150
151 friend bool operator==(const EdgeIterator&, const EdgeIterator&) = default;
152
153 private:
154 friend class Graph;
155
156 EdgeIterator(OuterIterator iterator, OuterIterator last)
157 : outer_(iterator), last_(last) {
158 if (outer_ != last_) {
159 inner_ = outer_->second.cbegin();
160 skipToEdge();
161 }
162 }
163
164 // Advances the position to the next neighbor larger than its own
165 // vertex, which is the one direction of an edge that this iterator
166 // reports. Past the last one, the inner iterator is reset to its
167 // value-initialized state so that it compares equal to the end
168 // iterator's, whatever adjacency set the walk stopped in.
169 void skipToEdge() {
170 while (outer_ != last_) {
171 if (inner_ == outer_->second.cend()) {
172 ++outer_;
173 if (outer_ == last_) {
174 break;
175 }
176 inner_ = outer_->second.cbegin();
177 } else if (outer_->first < *inner_) {
178 return;
179 } else {
180 ++inner_;
181 }
182 }
183 inner_ = InnerIterator{};
184 }
185
186 OuterIterator outer_;
187 OuterIterator last_;
188 InnerIterator inner_;
189 };
190
193
195 Graph() = default;
196
202 explicit Graph(const std::vector<std::array<Vertex, 2>>& edges) {
203 for (const auto& [u, v] : edges) {
204 addEdge(u, v);
205 }
206 }
207
213 void addVertex(const Vertex& vertex) {
214 adjacency_.try_emplace(vertex);
215 }
216
225 void addEdge(const Vertex& u, const Vertex& v) {
226 if (u != v) {
227 adjacency_[u].insert(v);
228 adjacency_[v].insert(u);
229 }
230 }
231
238 [[nodiscard]] bool containsVertex(const Vertex& vertex) const {
239 return adjacency_.contains(vertex);
240 }
241
249 [[nodiscard]] bool containsEdge(const Vertex& u, const Vertex& v) const {
250 const auto uIt = adjacency_.find(u);
251 return uIt != adjacency_.end() && uIt->second.contains(v);
252 }
253
260 [[nodiscard]] int degree(const Vertex& vertex) const {
261 const auto vertexIt = adjacency_.find(vertex);
262 if (vertexIt == adjacency_.end()) {
263 return -1;
264 }
265 return static_cast<int>(vertexIt->second.size());
266 }
267
273 [[nodiscard]] int maxDegree() const {
274 int result = -1;
275 for (const auto& entry : adjacency_) {
276 result = std::max(result, static_cast<int>(entry.second.size()));
277 }
278 return result;
279 }
280
282 [[nodiscard]] int vertexCount() const {
283 return static_cast<int>(adjacency_.size());
284 }
285
287 [[nodiscard]] int edgeCount() const {
288 std::size_t directedEdgeCount = 0;
289 for (const auto& entry : adjacency_) {
290 directedEdgeCount += entry.second.size();
291 }
292 assert(directedEdgeCount % 2 == 0);
293 return static_cast<int>(directedEdgeCount / 2);
294 }
295
304 void removeEdge(const Vertex& u, const Vertex& v) {
305 const auto uIt = adjacency_.find(u);
306 if (uIt == adjacency_.end() || !uIt->second.contains(v)) {
307 return;
308 }
309 uIt->second.erase(v);
310 adjacency_.at(v).erase(u);
311 }
312
318 void removeVertex(const Vertex& vertex) {
319 const auto vertexIt = adjacency_.find(vertex);
320 if (vertexIt == adjacency_.end()) {
321 return;
322 }
323
324 // Copy because erasing incident edges invalidates adjacency iterators.
325 const NeighborSet neighbors = vertexIt->second;
326 for (const Vertex& neighbor : neighbors) {
327 adjacency_.at(neighbor).erase(vertex);
328 }
329 adjacency_.erase(vertexIt);
330 }
331
333 void clear() {
334 adjacency_.clear();
335 }
336
347 [[nodiscard]] auto vertices() const {
348 return std::ranges::subrange(begin(), end());
349 }
350
365 [[nodiscard]] auto edges() const
366 requires std::totally_ordered<Vertex>
367 {
368 return std::ranges::subrange(
369 EdgeIterator(adjacency_.cbegin(), adjacency_.cend()),
370 EdgeIterator(adjacency_.cend(), adjacency_.cend())
371 );
372 }
373
381 [[nodiscard]] const NeighborSet& neighbors(const Vertex& vertex) const {
382 return adjacency_.at(vertex);
383 }
384
392 [[nodiscard]] NeighborSet closedNeighbors(const Vertex& vertex) const {
393 NeighborSet result = adjacency_.at(vertex);
394 result.insert(vertex);
395 return result;
396 }
397
407 [[nodiscard]] std::vector<Vertex> bfs(const Vertex& vertex, int maxVertices = 0) const {
408 if (!containsVertex(vertex) || maxVertices < 0) {
409 return {};
410 }
411
412 const std::size_t limit = maxVertices == 0
413 ? adjacency_.size()
414 : static_cast<std::size_t>(maxVertices);
415
416 NeighborSet visited;
417 std::vector<Vertex> result;
418 std::queue<Vertex> queue;
419
420 visited.insert(vertex);
421 queue.push(vertex);
422 while (!queue.empty() && result.size() < limit) {
423 const Vertex current = queue.front();
424 queue.pop();
425 result.push_back(current);
426
427 for (const Vertex& neighbor : neighbors(current)) {
428 if (visited.insert(neighbor).second) {
429 queue.push(neighbor);
430 }
431 }
432 }
433
434 return result;
435 }
436
446 [[nodiscard]] std::vector<std::vector<Vertex>> components() const {
447 NeighborSet visited;
448 std::vector<std::vector<Vertex>> result;
449
450 for (const auto& entry : adjacency_) {
451 if (visited.contains(entry.first)) {
452 continue;
453 }
454
455 result.push_back(bfs(entry.first));
456 visited.insert(result.back().begin(), result.back().end());
457 }
458
459 std::sort(result.begin(), result.end(), [](const auto& a, const auto& b) {
460 return a.size() > b.size();
461 });
462 return result;
463 }
464
479 [[nodiscard]] std::vector<std::vector<Vertex>> biconnectedComponents() const {
480 struct SearchData {
481 std::size_t index;
482 std::size_t low;
483 };
484
485 struct Frame {
486 Vertex vertex;
487 std::optional<Vertex> parent;
488 typename NeighborSet::const_iterator nextNeighbor;
489 typename NeighborSet::const_iterator endNeighbor;
490 };
491
492 std::size_t timer = 0;
493 std::unordered_map<Vertex, SearchData> searchData;
494 std::vector<std::pair<Vertex, Vertex>> edgeStack;
495 std::vector<std::vector<Vertex>> result;
496
497 for (const auto& entry : adjacency_) {
498 const Vertex& start = entry.first;
499 if (searchData.contains(start)) {
500 continue;
501 }
502
503 searchData.emplace(start, SearchData{timer, timer});
504 ++timer;
505
506 std::stack<Frame> stack;
507 stack.push(Frame{start, std::nullopt, entry.second.cbegin(), entry.second.cend()});
508
509 while (!stack.empty()) {
510 Frame& frame = stack.top();
511 const Vertex vertex = frame.vertex;
512
513 if (frame.nextNeighbor != frame.endNeighbor) {
514 const Vertex neighbor = *frame.nextNeighbor;
515 ++frame.nextNeighbor;
516
517 if (frame.parent.has_value() && neighbor == *frame.parent) {
518 continue;
519 }
520
521 const auto neighborData = searchData.find(neighbor);
522 if (neighborData == searchData.end()) {
523 edgeStack.emplace_back(vertex, neighbor);
524 searchData.emplace(neighbor, SearchData{timer, timer});
525 ++timer;
526
527 const NeighborSet& nextNeighbors = adjacency_.at(neighbor);
528 stack.push(Frame{
529 neighbor,
530 vertex,
531 nextNeighbors.cbegin(),
532 nextNeighbors.cend(),
533 });
534 } else if (neighborData->second.index < searchData.at(vertex).index) {
535 SearchData& vertexData = searchData.at(vertex);
536 vertexData.low = std::min(vertexData.low, neighborData->second.index);
537 edgeStack.emplace_back(vertex, neighbor);
538 }
539 continue;
540 }
541
542 const std::optional<Vertex> parent = frame.parent;
543 stack.pop();
544 if (!parent.has_value()) {
545 continue;
546 }
547
548 SearchData& parentData = searchData.at(*parent);
549 const SearchData& vertexData = searchData.at(vertex);
550 parentData.low = std::min(parentData.low, vertexData.low);
551
552 if (vertexData.low >= parentData.index) {
553 NeighborSet addedVertices;
554 std::vector<Vertex> component;
555 bool foundTreeEdge = false;
556
557 do {
558 assert(!edgeStack.empty());
559 const auto edge = std::move(edgeStack.back());
560 edgeStack.pop_back();
561
562 if (addedVertices.insert(edge.first).second) {
563 component.push_back(edge.first);
564 }
565 if (addedVertices.insert(edge.second).second) {
566 component.push_back(edge.second);
567 }
568 foundTreeEdge = edge.first == *parent && edge.second == vertex;
569 } while (!foundTreeEdge);
570
571 result.push_back(std::move(component));
572 }
573 }
574 }
575
576 std::sort(result.begin(), result.end(), [](const auto& a, const auto& b) {
577 return a.size() > b.size();
578 });
579 return result;
580 }
581
597 [[nodiscard]] std::vector<std::vector<Vertex>> cliqueCover() const {
598 // DSATUR works with vertex indices, so the vertices are materialized
599 // once rather than taken from the lazy view.
600 const std::vector<Vertex> graphVertices(begin(), end());
601 const std::size_t vertexCount = graphVertices.size();
602 const std::size_t uncolored = vertexCount;
603
604 std::vector<std::size_t> colors(vertexCount, uncolored);
605 std::vector<std::size_t> complementDegrees(vertexCount);
606 std::vector<std::unordered_set<std::size_t>> neighborColors(vertexCount);
607 std::vector<std::vector<Vertex>> result;
608
609 for (std::size_t i = 0; i < vertexCount; ++i) {
610 complementDegrees[i] = vertexCount - 1 - adjacency_.at(graphVertices[i]).size();
611 }
612
613 for (std::size_t coloredCount = 0; coloredCount < vertexCount; ++coloredCount) {
614 std::size_t selected = uncolored;
615 for (std::size_t i = 0; i < vertexCount; ++i) {
616 if (colors[i] != uncolored) {
617 continue;
618 }
619
620 if (selected == uncolored ||
621 neighborColors[i].size() > neighborColors[selected].size() ||
622 (neighborColors[i].size() == neighborColors[selected].size() &&
623 complementDegrees[i] > complementDegrees[selected])) {
624 selected = i;
625 }
626 }
627 assert(selected != uncolored);
628
629 std::size_t color = 0;
630 while (neighborColors[selected].contains(color)) {
631 ++color;
632 }
633 colors[selected] = color;
634
635 if (color == result.size()) {
636 result.emplace_back();
637 }
638 result[color].push_back(graphVertices[selected]);
639
640 const NeighborSet& selectedNeighbors = adjacency_.at(graphVertices[selected]);
641 for (std::size_t i = 0; i < vertexCount; ++i) {
642 if (colors[i] == uncolored && i != selected &&
643 !selectedNeighbors.contains(graphVertices[i])) {
644 neighborColors[i].insert(color);
645 }
646 }
647 }
648
649 std::sort(result.begin(), result.end(), [](const auto& a, const auto& b) {
650 return a.size() > b.size();
651 });
652 return result;
653 }
654
667 [[nodiscard]] std::vector<Vertex> independentSet() const {
668 std::vector<Vertex> orderedVertices(begin(), end());
669 std::sort(orderedVertices.begin(), orderedVertices.end(), [this](
670 const Vertex& a,
671 const Vertex& b
672 ) {
673 return adjacency_.at(a).size() < adjacency_.at(b).size();
674 });
675
676 NeighborSet selected;
677 std::vector<Vertex> result;
678 for (const Vertex& vertex : orderedVertices) {
679 const NeighborSet& vertexNeighbors = adjacency_.at(vertex);
680 if (std::ranges::none_of(vertexNeighbors, [&selected](const Vertex& neighbor) {
681 return selected.contains(neighbor);
682 })) {
683 selected.insert(vertex);
684 result.push_back(vertex);
685 }
686 }
687 return result;
688 }
689
712 template <class WeightFunction>
713 [[nodiscard]] Graph spanningTree(WeightFunction weight) const {
714 using Weight = std::invoke_result_t<WeightFunction&, const Vertex&, const Vertex&>;
715
716 struct Candidate {
717 Weight weight;
718 Vertex from;
719 Vertex to;
720 };
721
722 // A priority_queue pops its largest element, so order edges by
723 // decreasing weight to obtain the lightest frontier edge.
724 const auto heavier = [](const Candidate& a, const Candidate& b) {
725 return b.weight < a.weight;
726 };
727
728 Graph result;
729 NeighborSet visited;
730 std::priority_queue<Candidate, std::vector<Candidate>, decltype(heavier)> frontier(heavier);
731
732 const auto pushIncidentEdges = [&](const Vertex& vertex) {
733 for (const Vertex& neighbor : adjacency_.at(vertex)) {
734 if (!visited.contains(neighbor)) {
735 frontier.push(Candidate{weight(vertex, neighbor), vertex, neighbor});
736 }
737 }
738 };
739
740 for (const auto& entry : adjacency_) {
741 if (visited.contains(entry.first)) {
742 continue;
743 }
744
745 // Grow one tree per connected component. The frontier is empty
746 // again once a component is exhausted, so it can be reused.
747 visited.insert(entry.first);
748 result.addVertex(entry.first);
749 pushIncidentEdges(entry.first);
750
751 while (!frontier.empty()) {
752 const Candidate best = frontier.top();
753 frontier.pop();
754 if (!visited.insert(best.to).second) {
755 continue;
756 }
757 result.addEdge(best.from, best.to);
758 pushIncidentEdges(best.to);
759 }
760 }
761
762 return result;
763 }
764
793 template <class WeightFunction>
794 [[nodiscard]] std::vector<Vertex> shortestPath(
795 const Vertex& source,
796 const Vertex& target,
797 WeightFunction weight
798 ) const {
799 using Weight = std::invoke_result_t<WeightFunction&, const Vertex&, const Vertex&>;
800
801 struct Candidate {
802 Weight weight;
803 Vertex from;
804 Vertex to;
805 };
806
807 if (!containsVertex(source) || !containsVertex(target)) {
808 return {};
809 }
810 if (source == target) {
811 return {source};
812 }
813
814 // A priority_queue pops its largest element, so order candidates by
815 // decreasing distance to obtain the closest unsettled vertex.
816 const auto farther = [](const Candidate& a, const Candidate& b) {
817 return b.weight < a.weight;
818 };
819
820 NeighborSet settled;
821 std::unordered_map<Vertex, Vertex> parent;
822 std::priority_queue<Candidate, std::vector<Candidate>, decltype(farther)> frontier(farther);
823
824 const auto pushIncidentEdges = [&](const Vertex& vertex, const Candidate& candidate) {
825 for (const Vertex& neighbor : adjacency_.at(vertex)) {
826 if (!settled.contains(neighbor)) {
827 frontier.push(Candidate{
828 candidate.weight + weight(vertex, neighbor),
829 vertex,
830 neighbor,
831 });
832 }
833 }
834 };
835
836 // Seeding the frontier with the edges leaving the source, rather than
837 // with the source itself, keeps every distance a sum of edge weights:
838 // the weight type needs no zero of its own.
839 settled.insert(source);
840 for (const Vertex& neighbor : adjacency_.at(source)) {
841 frontier.push(Candidate{weight(source, neighbor), source, neighbor});
842 }
843
844 while (!frontier.empty()) {
845 const Candidate best = frontier.top();
846 frontier.pop();
847 if (!settled.insert(best.to).second) {
848 continue;
849 }
850 parent.emplace(best.to, best.from);
851
852 if (best.to == target) {
853 std::vector<Vertex> result{target};
854 while (result.back() != source) {
855 result.push_back(parent.at(result.back()));
856 }
857 std::reverse(result.begin(), result.end());
858 return result;
859 }
860
861 pushIncidentEdges(best.to, best);
862 }
863
864 return {};
865 }
866
899 template <class WeightFunction, class LowerBoundFunction>
900 [[nodiscard]] std::vector<Vertex> shortestPath(
901 const Vertex& source,
902 const Vertex& target,
903 WeightFunction weight,
904 LowerBoundFunction lowerBound
905 ) const {
906 using Weight =
907 std::invoke_result_t<WeightFunction&, const Vertex&, const Vertex&>;
908
909 struct Candidate {
910 Weight distance;
911 Weight estimate;
912 Vertex vertex;
913 };
914
915 if (!containsVertex(source) || !containsVertex(target)) {
916 return {};
917 }
918 if (source == target) {
919 return {source};
920 }
921
922 // A priority_queue pops its largest element, so order candidates by
923 // decreasing estimated total distance to obtain the most promising
924 // unsettled vertex.
925 const auto farther = [](const Candidate& a, const Candidate& b) {
926 return b.estimate < a.estimate;
927 };
928
929 std::unordered_map<Vertex, Weight> distance;
930 std::unordered_map<Vertex, Vertex> parent;
931 std::priority_queue<Candidate, std::vector<Candidate>, decltype(farther)>
932 frontier(farther);
933
934 const auto relax = [&](const Vertex& from,
935 const Vertex& to,
936 const Weight& fromDistance) {
937 if (to == source) {
938 return;
939 }
940
941 const Weight newDistance = fromDistance + weight(from, to);
942 const auto known = distance.find(to);
943 if (known != distance.end() && !(newDistance < known->second)) {
944 return;
945 }
946
947 distance.insert_or_assign(to, newDistance);
948 parent.insert_or_assign(to, from);
949 frontier.push(Candidate{
950 newDistance,
951 newDistance + lowerBound(to, target),
952 to,
953 });
954 };
955
956 // As in the Dijkstra overload, seed with the source's edges so the
957 // weight type does not need a default-constructed zero.
958 for (const Vertex& neighbor : adjacency_.at(source)) {
959 const Weight neighborDistance = weight(source, neighbor);
960 distance.emplace(neighbor, neighborDistance);
961 parent.emplace(neighbor, source);
962 frontier.push(Candidate{
963 neighborDistance,
964 neighborDistance + lowerBound(neighbor, target),
965 neighbor,
966 });
967 }
968
969 while (!frontier.empty()) {
970 const Candidate best = frontier.top();
971 frontier.pop();
972
973 const auto known = distance.find(best.vertex);
974 if (known == distance.end() || known->second < best.distance) {
975 continue;
976 }
977
978 if (best.vertex == target) {
979 std::vector<Vertex> result{target};
980 while (result.back() != source) {
981 result.push_back(parent.at(result.back()));
982 }
983 std::reverse(result.begin(), result.end());
984 return result;
985 }
986
987 for (const Vertex& neighbor : adjacency_.at(best.vertex)) {
988 relax(best.vertex, neighbor, best.distance);
989 }
990 }
991
992 return {};
993 }
994
996 [[nodiscard]] iterator begin() {
997 return iterator(adjacency_.cbegin());
998 }
999
1001 [[nodiscard]] iterator end() {
1002 return iterator(adjacency_.cend());
1003 }
1004
1006 [[nodiscard]] const_iterator begin() const {
1007 return const_iterator(adjacency_.cbegin());
1008 }
1009
1011 [[nodiscard]] const_iterator end() const {
1012 return const_iterator(adjacency_.cend());
1013 }
1014
1016 [[nodiscard]] const_iterator cbegin() const {
1017 return begin();
1018 }
1019
1021 [[nodiscard]] const_iterator cend() const {
1022 return end();
1023 }
1024
1025private:
1026 AdjacencyMap adjacency_;
1027};
1028
1029} // namespace pgl
Forward iterator over the undirected edges of a graph.
Definition graph.hpp:115
EdgeType value_type
Definition graph.hpp:126
EdgeIterator & operator++()
Advances to the next edge.
Definition graph.hpp:138
std::ptrdiff_t difference_type
Definition graph.hpp:125
friend bool operator==(const EdgeIterator &, const EdgeIterator &)=default
reference operator*() const
Returns the current edge, smaller endpoint first.
Definition graph.hpp:133
EdgeType reference
Definition graph.hpp:127
std::input_iterator_tag iterator_category
Definition graph.hpp:123
std::forward_iterator_tag iterator_concept
Definition graph.hpp:124
EdgeIterator()=default
Creates an iterator with no associated graph.
EdgeIterator operator++(int)
Advances to the next edge and returns the previous position.
Definition graph.hpp:145
friend class Graph
Definition graph.hpp:154
Forward iterator over the vertices of a graph.
Definition graph.hpp:57
reference operator*() const
Returns the current vertex.
Definition graph.hpp:72
Iterator & operator++()
Advances to the next vertex.
Definition graph.hpp:82
pointer operator->() const
Returns a pointer to the current vertex.
Definition graph.hpp:77
std::forward_iterator_tag iterator_concept
Definition graph.hpp:62
Iterator operator++(int)
Advances to the next vertex and returns the previous position.
Definition graph.hpp:88
std::forward_iterator_tag iterator_category
Definition graph.hpp:61
const Vertex * pointer
Definition graph.hpp:65
const Vertex & reference
Definition graph.hpp:66
friend bool operator==(const Iterator &, const Iterator &)=default
Iterator()=default
Creates an iterator with no associated graph.
Vertex value_type
Definition graph.hpp:64
friend class Graph
Definition graph.hpp:97
std::ptrdiff_t difference_type
Definition graph.hpp:63
void removeVertex(const Vertex &vertex)
Removes a vertex and every incident edge.
Definition graph.hpp:318
bool containsVertex(const Vertex &vertex) const
Tests whether a vertex is present.
Definition graph.hpp:238
const_iterator begin() const
Returns a const iterator to the first vertex.
Definition graph.hpp:1006
void addVertex(const Vertex &vertex)
Adds a vertex if it is not already present.
Definition graph.hpp:213
int vertexCount() const
Returns the number of vertices.
Definition graph.hpp:282
const_iterator cend() const
Returns the const end iterator.
Definition graph.hpp:1021
std::vector< Vertex > independentSet() const
Computes a maximal independent set greedily from low-degree vertices.
Definition graph.hpp:667
std::vector< Vertex > shortestPath(const Vertex &source, const Vertex &target, WeightFunction weight, LowerBoundFunction lowerBound) const
Computes a shortest path between two vertices using the A* algorithm.
Definition graph.hpp:900
std::vector< std::vector< Vertex > > components() const
Returns the graph's connected components.
Definition graph.hpp:446
const_iterator end() const
Returns the const end iterator.
Definition graph.hpp:1011
std::unordered_set< Vertex > NeighborSet
Definition graph.hpp:46
Graph spanningTree(WeightFunction weight) const
Computes a minimum spanning forest using Prim's algorithm.
Definition graph.hpp:713
Graph(const std::vector< std::array< Vertex, 2 > > &edges)
Creates a graph from a list of undirected edges.
Definition graph.hpp:202
iterator begin()
Returns an iterator to the first vertex.
Definition graph.hpp:996
std::vector< std::vector< Vertex > > biconnectedComponents() const
Returns the vertex-biconnected blocks of the graph.
Definition graph.hpp:479
Vertex VertexType
Definition graph.hpp:43
std::vector< Vertex > bfs(const Vertex &vertex, int maxVertices=0) const
Traverses a connected component in breadth-first order.
Definition graph.hpp:407
int degree(const Vertex &vertex) const
Returns the degree of a vertex.
Definition graph.hpp:260
std::vector< std::vector< Vertex > > cliqueCover() const
Computes a vertex clique cover using the DSATUR heuristic.
Definition graph.hpp:597
Iterator const_iterator
Definition graph.hpp:192
void clear()
Removes every vertex and edge.
Definition graph.hpp:333
std::vector< Vertex > shortestPath(const Vertex &source, const Vertex &target, WeightFunction weight) const
Computes a shortest path between two vertices using Dijkstra's algorithm.
Definition graph.hpp:794
const NeighborSet & neighbors(const Vertex &vertex) const
Returns the neighbors of a vertex.
Definition graph.hpp:381
NeighborSet closedNeighbors(const Vertex &vertex) const
Returns a vertex together with all of its neighbors.
Definition graph.hpp:392
int edgeCount() const
Returns the number of undirected edges.
Definition graph.hpp:287
iterator end()
Returns the end iterator.
Definition graph.hpp:1001
auto edges() const
Returns a lazy view over the undirected edges.
Definition graph.hpp:365
void addEdge(const Vertex &u, const Vertex &v)
Adds an undirected edge and its endpoints.
Definition graph.hpp:225
std::array< Vertex, 2 > EdgeType
Definition graph.hpp:49
bool containsEdge(const Vertex &u, const Vertex &v) const
Tests whether an undirected edge is present.
Definition graph.hpp:249
void removeEdge(const Vertex &u, const Vertex &v)
Removes an undirected edge if it is present.
Definition graph.hpp:304
Graph()=default
Creates an empty graph.
auto vertices() const
Returns a lazy view over the vertices.
Definition graph.hpp:347
const_iterator cbegin() const
Returns a const iterator to the first vertex.
Definition graph.hpp:1016
Iterator iterator
Definition graph.hpp:191
int maxDegree() const
Returns the largest vertex degree.
Definition graph.hpp:273
Enumeration of the integer grid points a shape contains.
Definition arrangement.hpp:67
@ edge
Definition bitmatrix.hpp:37
@ vertex
Definition bitmatrix.hpp:37