Google OR-Tools v9.15
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
minimum_spanning_tree.h
Go to the documentation of this file.
1// Copyright 2010-2025 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14#ifndef ORTOOLS_GRAPH_MINIMUM_SPANNING_TREE_H_
15#define ORTOOLS_GRAPH_MINIMUM_SPANNING_TREE_H_
16
17#include <limits>
18#include <vector>
19
20#include "absl/types/span.h"
24
25namespace operations_research {
26
27// Implementation of Kruskal's mininumum spanning tree algorithm (c.f.
28// https://en.wikipedia.org/wiki/Kruskal%27s_algorithm).
29// Returns the index of the arcs appearing in the tree; will return a forest if
30// the graph is disconnected. Nodes without any arcs will be ignored.
31// Each arc of the graph is interpreted as an undirected arc.
32// Complexity of the algorithm is O(E * log(E)) where E is the number of arcs
33// in the graph. Memory usage is O(E * log(E)).
34
35// TODO(user): Add a global Minimum Spanning Tree API automatically switching
36// between Prim and Kruskal depending on problem size.
37
38// Version taking sorted graph arcs. Allows somewhat incremental recomputation
39// of minimum spanning trees as most of the processing time is spent sorting
40// arcs.
41// Usage:
42// ListGraph<int, int> graph(...);
43// std::vector<int> sorted_arcs = ...;
44// std::vector<int> mst = BuildKruskalMinimumSpanningTreeFromSortedArcs(
45// graph, sorted_arcs);
46//
47template <typename Graph>
48std::vector<typename Graph::ArcIndex>
50 const Graph& graph,
51 absl::Span<const typename Graph::ArcIndex> sorted_arcs) {
52 using ArcIndex = typename Graph::ArcIndex;
53 const int num_arcs = graph.num_arcs();
54 int arc_index = 0;
55 std::vector<ArcIndex> tree_arcs;
56 if (graph.num_nodes() == 0) {
57 return tree_arcs;
58 }
59 const int expected_tree_size = graph.num_nodes() - 1;
60 tree_arcs.reserve(expected_tree_size);
62 components.SetNumberOfNodes(graph.num_nodes());
63 while (tree_arcs.size() != expected_tree_size && arc_index < num_arcs) {
64 const ArcIndex arc = sorted_arcs[arc_index];
65 const auto tail = graph.Tail(arc);
66 const auto head = graph.Head(arc);
67 if (!components.Connected(tail, head)) {
68 components.AddEdge(tail, head);
69 tree_arcs.push_back(arc);
70 }
71 ++arc_index;
72 }
73 return tree_arcs;
74}
75
76// Version taking an arc comparator to sort graph arcs.
77// Usage:
78// ListGraph<int, int> graph(...);
79// const auto arc_cost = [&graph](int arc) {
80// return f(graph.Tail(arc), graph.Head(arc));
81// };
82// std::vector<int> mst = BuildKruskalMinimumSpanningTree(
83// graph,
84// [&arc_cost](int a, int b) { return arc_cost(a) < arc_cost(b); });
85//
86template <typename Graph, typename ArcComparator>
87std::vector<typename Graph::ArcIndex> BuildKruskalMinimumSpanningTree(
88 const Graph& graph, const ArcComparator& arc_comparator) {
89 using ArcIndex = typename Graph::ArcIndex;
90 std::vector<ArcIndex> sorted_arcs(graph.num_arcs());
91 for (const ArcIndex arc : graph.AllForwardArcs()) {
92 sorted_arcs[arc] = arc;
93 }
94 std::sort(sorted_arcs.begin(), sorted_arcs.end(), arc_comparator);
95 return BuildKruskalMinimumSpanningTreeFromSortedArcs(graph, sorted_arcs);
96}
97
98// Implementation of Prim's mininumum spanning tree algorithm (c.f.
99// https://en.wikipedia.org/wiki/Prim's_algorithm) on undirected connected
100// graphs.
101// Returns the index of the arcs appearing in the tree.
102// Complexity of the algorithm is O(E * log(V)) where E is the number of arcs
103// in the graph, V is the number of vertices. Memory usage is O(V) + memory
104// taken by the graph.
105// Usage:
106// ListGraph<int, int> graph(...);
107// const auto arc_cost = [&graph](int arc) -> int64_t {
108// return f(graph.Tail(arc), graph.Head(arc));
109// };
110// std::vector<int> mst = BuildPrimMinimumSpanningTree(graph, arc_cost);
111//
112template <typename Graph, typename ArcValue>
113std::vector<typename Graph::ArcIndex> BuildPrimMinimumSpanningTree(
114 const Graph& graph, const ArcValue& arc_value) {
115 using ArcIndex = typename Graph::ArcIndex;
116 using NodeIndex = typename Graph::NodeIndex;
117 using ArcValueType = decltype(arc_value(0));
118 std::vector<ArcIndex> tree_arcs;
119 if (graph.num_nodes() == 0) {
120 return tree_arcs;
121 }
122 const int expected_tree_size = graph.num_nodes() - 1;
123 tree_arcs.reserve(expected_tree_size);
124 std::vector<ArcIndex> node_neighbor(graph.num_nodes(), Graph::kNilArc);
125 std::vector<bool> node_active(graph.num_nodes(), true);
126
127 // This struct represents entries in the adjustable priority queue which
128 // maintains active nodes (not added to the tree yet) in decreasing insertion
129 // cost order. AdjustablePriorityQueue requires the existence of the
130 // SetHeapIndex and GetHeapIndex methods.
131 struct Entry {
132 void SetHeapIndex(int index) { heap_index = index; }
133 int GetHeapIndex() const { return heap_index; }
134 bool operator<(const Entry& other) const { return value > other.value; }
135
136 // In the typical case, `NodeIndex` is 4 bytes, so having fields in this
137 // order is optimal in terms of memory usage and cache locality across all
138 // values of `sizeof(ArcValueType)`.
139 ArcValueType value;
140 NodeIndex node;
141 int heap_index;
142 };
143
145 std::vector<Entry> entries;
146 std::vector<bool> touched_entry(graph.num_nodes(), false);
147 for (NodeIndex node : graph.AllNodes()) {
148 entries.push_back({.value = std::numeric_limits<ArcValueType>::max(),
149 .node = node,
150 .heap_index = -1});
151 }
152 entries[0].value = 0;
153 pq.Add(&entries[0]);
154 while (!pq.IsEmpty() && tree_arcs.size() != expected_tree_size) {
155 const Entry* best = pq.Top();
156 const NodeIndex node = best->node;
157 pq.Pop();
158 node_active[node] = false;
159 if (node_neighbor[node] != Graph::kNilArc) {
160 tree_arcs.push_back(node_neighbor[node]);
161 }
162 for (const ArcIndex arc : graph.OutgoingArcs(node)) {
163 const NodeIndex neighbor = graph.Head(arc);
164 if (node_active[neighbor]) {
165 const ArcValueType value = arc_value(arc);
166 Entry& entry = entries[neighbor];
167 if (value < entry.value || !touched_entry[neighbor]) {
168 node_neighbor[neighbor] = arc;
169 entry.value = value;
170 touched_entry[neighbor] = true;
171 if (pq.Contains(&entry)) {
172 pq.NoteChangedPriority(&entry);
173 } else {
174 pq.Add(&entry);
175 }
176 }
177 }
178 }
179 }
180 return tree_arcs;
181}
182
183} // namespace operations_research
184#endif // ORTOOLS_GRAPH_MINIMUM_SPANNING_TREE_H_
bool Contains(const T *val) const
bool Connected(int node1, int node2)
bool AddEdge(int node1, int node2)
IntegerRange< ArcIndex > AllForwardArcs() const
Definition graph.h:1154
NodeIndexType num_nodes() const
Definition graph.h:229
ArcIndexType num_arcs() const
Definition graph.h:233
IntegerRange< NodeIndex > AllNodes() const
Definition graph.h:1146
NodeIndexType Tail(ArcIndexType arc) const
Definition graph.h:1723
IntegerRange< ArcIndexType > OutgoingArcs(NodeIndexType node) const
Definition graph.h:1033
NodeIndexType Head(ArcIndexType arc) const
Definition graph.h:1715
OR-Tools root namespace.
BlossomGraph::NodeIndex NodeIndex
std::vector< typename Graph::ArcIndex > BuildPrimMinimumSpanningTree(const Graph &graph, const ArcValue &arc_value)
std::vector< typename Graph::ArcIndex > BuildKruskalMinimumSpanningTreeFromSortedArcs(const Graph &graph, absl::Span< const typename Graph::ArcIndex > sorted_arcs)
util::ReverseArcStaticGraph Graph
std::vector< typename Graph::ArcIndex > BuildKruskalMinimumSpanningTree(const Graph &graph, const ArcComparator &arc_comparator)