Google OR-Tools v9.11
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-2024 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 OR_TOOLS_GRAPH_MINIMUM_SPANNING_TREE_H_
15#define OR_TOOLS_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 using NodeIndex = typename Graph::NodeIndex;
54 const int num_arcs = graph.num_arcs();
55 int arc_index = 0;
56 std::vector<ArcIndex> tree_arcs;
57 if (graph.num_nodes() == 0) {
58 return tree_arcs;
59 }
60 const int expected_tree_size = graph.num_nodes() - 1;
61 tree_arcs.reserve(expected_tree_size);
63 components.SetNumberOfNodes(graph.num_nodes());
64 while (tree_arcs.size() != expected_tree_size && arc_index < num_arcs) {
65 const ArcIndex arc = sorted_arcs[arc_index];
66 const auto tail = graph.Tail(arc);
67 const auto head = graph.Head(arc);
68 if (!components.Connected(tail, head)) {
69 components.AddEdge(tail, head);
70 tree_arcs.push_back(arc);
71 }
72 ++arc_index;
73 }
74 return tree_arcs;
75}
76
77// Version taking an arc comparator to sort graph arcs.
78// Usage:
79// ListGraph<int, int> graph(...);
80// const auto arc_cost = [&graph](int arc) {
81// return f(graph.Tail(arc), graph.Head(arc));
82// };
83// std::vector<int> mst = BuildKruskalMinimumSpanningTree(
84// graph,
85// [&arc_cost](int a, int b) { return arc_cost(a) < arc_cost(b); });
86//
87template <typename Graph, typename ArcComparator>
88std::vector<typename Graph::ArcIndex> BuildKruskalMinimumSpanningTree(
89 const Graph& graph, const ArcComparator& arc_comparator) {
90 using ArcIndex = typename Graph::ArcIndex;
91 std::vector<ArcIndex> sorted_arcs(graph.num_arcs());
92 for (const ArcIndex arc : graph.AllForwardArcs()) {
93 sorted_arcs[arc] = arc;
94 }
95 std::sort(sorted_arcs.begin(), sorted_arcs.end(), arc_comparator);
97}
98
99// Implementation of Prim's mininumum spanning tree algorithm (c.f.
100// https://en.wikipedia.org/wiki/Prim's_algorithm) on undirected connected
101// graphs.
102// Returns the index of the arcs appearing in the tree.
103// Complexity of the algorithm is O(E * log(V)) where E is the number of arcs
104// in the graph, V is the number of vertices. Memory usage is O(V) + memory
105// taken by the graph.
106// Usage:
107// ListGraph<int, int> graph(...);
108// const auto arc_cost = [&graph](int arc) -> int64_t {
109// return f(graph.Tail(arc), graph.Head(arc));
110// };
111// std::vector<int> mst = BuildPrimMinimumSpanningTree(graph, arc_cost);
112//
113template <typename Graph, typename ArcValue>
114std::vector<typename Graph::ArcIndex> BuildPrimMinimumSpanningTree(
115 const Graph& graph, const ArcValue& arc_value) {
116 using ArcIndex = typename Graph::ArcIndex;
117 using NodeIndex = typename Graph::NodeIndex;
118 using ArcValueType = decltype(arc_value(0));
119 std::vector<ArcIndex> tree_arcs;
120 if (graph.num_nodes() == 0) {
121 return tree_arcs;
122 }
123 const int expected_tree_size = graph.num_nodes() - 1;
124 tree_arcs.reserve(expected_tree_size);
125 std::vector<ArcIndex> node_neighbor(graph.num_nodes(), Graph::kNilArc);
126 std::vector<bool> node_active(graph.num_nodes(), true);
127
128 // This struct represents entries in the adjustable priority queue which
129 // maintains active nodes (not added to the tree yet) in decreasing insertion
130 // cost order. AdjustablePriorityQueue requires the existence of the
131 // SetHeapIndex and GetHeapIndex methods.
132 struct Entry {
133 void SetHeapIndex(int index) { heap_index = index; }
134 int GetHeapIndex() const { return heap_index; }
135 bool operator<(const Entry& other) const { return value > other.value; }
136
137 NodeIndex node;
138 ArcValueType value;
139 int heap_index;
140 };
141
143 std::vector<Entry> entries;
144 std::vector<bool> touched_entry(graph.num_nodes(), false);
145 for (NodeIndex node : graph.AllNodes()) {
146 entries.push_back({node, std::numeric_limits<ArcValueType>::max(), -1});
147 }
148 entries[0].value = 0;
149 pq.Add(&entries[0]);
150 while (!pq.IsEmpty() && tree_arcs.size() != expected_tree_size) {
151 const Entry* best = pq.Top();
152 const NodeIndex node = best->node;
153 pq.Pop();
154 node_active[node] = false;
155 if (node_neighbor[node] != Graph::kNilArc) {
156 tree_arcs.push_back(node_neighbor[node]);
157 }
158 for (const ArcIndex arc : graph.OutgoingArcs(node)) {
159 const NodeIndex neighbor = graph.Head(arc);
160 if (node_active[neighbor]) {
161 const ArcValueType value = arc_value(arc);
162 Entry& entry = entries[neighbor];
163 if (value < entry.value || !touched_entry[neighbor]) {
164 node_neighbor[neighbor] = arc;
165 entry.value = value;
166 touched_entry[neighbor] = true;
167 if (pq.Contains(&entry)) {
168 pq.NoteChangedPriority(&entry);
169 } else {
170 pq.Add(&entry);
171 }
172 }
173 }
174 }
175 }
176 return tree_arcs;
177}
178
179} // namespace operations_research
180#endif // OR_TOOLS_GRAPH_MINIMUM_SPANNING_TREE_H_
bool Contains(const T *val) const
void Pop()
If there are ties for the top, this returns all of them.
A connected components finder that only works on dense ints.
bool Connected(int node1, int node2)
bool AddEdge(int node1, int node2)
GraphType graph
int64_t value
int arc
int index
In SWIG mode, we don't want anything besides these top-level includes.
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)
std::vector< typename Graph::ArcIndex > BuildKruskalMinimumSpanningTree(const Graph &graph, const ArcComparator &arc_comparator)
int head
int tail