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