Fishnet 2.0.1
Framework for Graph-Based Analysis of GIS Vector Data
Loading...
Searching...
No Matches
MSTAlgorithm.hpp
Go to the documentation of this file.
1#pragma once
2#include <vector>
3#include <algorithm>
4#include <ranges>
5#include <unordered_map>
6#include <optional>
7#include <type_traits>
8#include <fishnet/Graph.hpp>
10#include <fishnet/Option.hpp>
11
13
14namespace __impl {
15
20class UnionFind {
21private:
22 std::vector<size_t> parent;
23 std::vector<size_t> rank;
24
25public:
26 explicit UnionFind(size_t n) : parent(n), rank(n, 0) {
27 for (size_t i = 0; i < n; ++i) {
28 parent[i] = i;
29 }
30 }
31
32 size_t find(size_t x) {
33 if (parent[x] != x) {
34 parent[x] = find(parent[x]);
35 }
36 return parent[x];
37 }
38
39 bool unite(size_t a, size_t b) {
40 size_t rootA = find(a);
41 size_t rootB = find(b);
42 if (rootA == rootB) {
43 return false;
44 }
45 if (rank[rootA] < rank[rootB]) {
46 std::swap(rootA, rootB);
47 }
48 parent[rootB] = rootA;
49 if (rank[rootA] == rank[rootB]) {
50 rank[rootA]++;
51 }
52 return true;
53 }
54
55 bool connected(size_t a, size_t b) {
56 return find(a) == find(b);
57 }
58};
59
70template<Graph G, typename W>
71requires std::totally_ordered<std::invoke_result_t<W, const typename G::node_type&, const typename G::node_type&>>
72static fishnet::Option<G> kruskalImpl(
73 const G& graph,
74 W const& weightFunction,
75 G && mst
76) {
77 using N = typename G::node_type;
78 using E = typename G::edge_type;
79 using H = typename G::adj_container_type::hash_function;
80 using Eq = typename G::adj_container_type::equality_predicate;
81
82 auto nodes = graph.getNodes();
83 size_t nodeCount = std::ranges::distance(nodes);
84
85 if (nodeCount == 0) {
86 return std::move(mst);
87 }
88
89 if (nodeCount == 1) {
90 for (const auto& node : nodes) {
91 mst.addNode(node);
92 }
93 return std::move(mst);
94 }
95
96 // node -> index mapping
97 std::unordered_map<N, size_t, H, Eq> nodeToIndex;
98 nodeToIndex.reserve(nodeCount);
99 size_t idx = 0;
100 for (const auto& node : nodes) {
101 nodeToIndex[node] = idx++;
102 }
103
104 std::vector<E> edges = fishnet::util::toVector(graph.getEdges());
105 auto weightComparator = [&weightFunction](const E& lhs, const E& rhs) {
106 return weightFunction(lhs.getFrom(), lhs.getTo()) < weightFunction(rhs.getFrom(), rhs.getTo());
107 };
108 std::ranges::sort(edges, weightComparator);
109
110 UnionFind uf(nodeCount);
111 size_t edgesAdded = 0;
112 size_t targetEdges = nodeCount - 1;
113
114 for (const auto& edge : edges) {
115 size_t fromIdx = nodeToIndex.at(edge.getFrom());
116 size_t toIdx = nodeToIndex.at(edge.getTo());
117 if (not uf.connected(fromIdx, toIdx)) {
118 uf.unite(fromIdx, toIdx);
119 mst.addEdge(edge.getFrom(), edge.getTo());
120 edgesAdded++;
121 if (edgesAdded == targetEdges) {
122 break;
123 }
124 }
125 }
126 // Check if graph is connected (MST has exactly n-1 edges)
127 if (edgesAdded != targetEdges) {
128 return std::nullopt;
129 }
130 return std::move(mst);
131}
132
133} // namespace __impl
134
147template<Graph G, typename W>
148requires std::totally_ordered<std::invoke_result_t<W, const typename G::node_type&, const typename G::node_type&>> && std::is_default_constructible_v<G>
150 const G& graph,
151 W const& weightFunction
152) {
153 return __impl::kruskalImpl<G, W>(graph, weightFunction, G());
154}
155
169template<Graph G, typename W>
170requires std::totally_ordered<std::invoke_result_t<W, const typename G::node_type&, const typename G::node_type&>>
172 const G& graph,
173 W const& weightFunction,
174 G && outputGraph
175) {
176 return __impl::kruskalImpl<G, W>(graph, weightFunction, std::forward<G>(outputGraph));
177}
178
179} // namespace fishnet::graph::MST
Definition Option.hpp:13
Definition MSTAlgorithm.hpp:12
fishnet::Option< G > kruskal(const G &graph, W const &weightFunction)
Compute the Minimum Spanning Tree of a graph using Kruskal's algorithm.
Definition MSTAlgorithm.hpp:149
constexpr auto toVector(R &&range)
Definition CollectionConcepts.hpp:64