Files
ortools-clone/ortools/graph/connectivity.h
Corentin Le Molgat f2989d319d Replace non ASCII character '–' in comments
- Crash on ubuntu 16.04 during 'make java'...
2018-09-10 13:37:04 +02:00

171 lines
6.0 KiB
C++

// Copyright 2010-2017 Google
// 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.
// Graph connectivity algorithm for undirected graphs.
// Memory consumption: O(n) where m is the number of arcs and n the number
// of nodes.
// TODO(user): add depth-first-search based connectivity for directed graphs.
// TODO(user): add depth-first-search based biconnectivity for directed graphs.
#ifndef OR_TOOLS_GRAPH_CONNECTIVITY_H_
#define OR_TOOLS_GRAPH_CONNECTIVITY_H_
#include <vector>
#include "ortools/base/integral_types.h"
#include "ortools/base/logging.h"
#include "ortools/base/macros.h"
namespace operations_research {
// Template class implementing a Union-Find algorithm with path compression for
// maintaining the connected components of a graph.
// See Cormen et al. 2nd Edition. MIT Press, 2001. ISBN 0-262-03293-7.
// Chapter 21: Data structures for Disjoint Sets, pp. 498-524.
// and Tarjan (1975). Efficiency of a Good But Not Linear Set
// Union Algorithm. Journal of the ACM 22(2):215-225
// It is implemented as a template so that the size of NodeIndex can be chosen
// depending on the size of the graphs considered.
// The main interest is that arcs do not need to be kept. Thus the memory
// complexity is O(n) where n is the number of nodes in the graph.
// The complexity of this algorithm is O(n . alpha(n)) where alpha(n) is
// the inverse Ackermann function. alpha(n) <= log(log(log(..log(log(n))..)
// In practice alpha(n) <= 5.
// See Tarjan and van Leeuwen (1984). Worst-case analysis of set union
// algorithms. Journal of the ACM 31(2):245-281.
//
// Usage example:
// ConnectedComponents<int, int> components;
// components.Init(num_nodes);
// for (int arc = 0; arc < num_arcs; ++arc) {
// components.AddArc(tail[arc], head[arc]);
// }
// int num_connected_components = components.GetNumberOfConnectedComponents();
// if (num_connected_components == 1) {
// // Graph is completely connected.
// }
// // Group the nodes in the same connected component together.
// // group[class_number][i] contains the i-th node in group class_number.
// std::unordered_map<int, std::vector<int> > group(num_connected_components);
// for (int node = 0; node < num_nodes; ++node) {
// group[components.GetClassRepresentative(node)].push_back(node);
// }
//
// Keywords: graph, connected components.
template <typename NodeIndex, typename ArcIndex>
class ConnectedComponents {
public:
ConnectedComponents() : num_nodes_(0), class_(), class_size_() {}
// Reserves memory for num_nodes and resets the data structures.
void Init(NodeIndex num_nodes) {
CHECK_GE(num_nodes, 0);
num_nodes_ = num_nodes;
class_.resize(num_nodes_);
class_size_.assign(num_nodes_, 1);
for (NodeIndex node = 0; node < num_nodes_; ++node) {
class_[node] = node;
}
}
// Adds the information that NodeIndex tail and NodeIndex head are connected.
void AddArc(NodeIndex tail, NodeIndex head) {
const NodeIndex tail_class = CompressPath(tail);
const NodeIndex head_class = CompressPath(head);
if (tail_class != head_class) {
MergeClasses(tail_class, head_class);
}
}
// Adds a complete StarGraph to the object. Note that Depth-First Search
// is a better algorithm for finding connected components on graphs.
// TODO(user): implement Depth-First Search-based connected components finder.
template <typename Graph>
void AddGraph(const Graph& graph) {
Init(graph.num_nodes());
for (NodeIndex tail = 0; tail < graph.num_nodes(); ++tail) {
for (typename Graph::OutgoingArcIterator it(graph, tail); it.Ok();
it.Next()) {
AddArc(tail, graph.Head(it.Index()));
}
}
}
// Compresses the path for node.
NodeIndex CompressPath(NodeIndex node) {
CheckNodeBounds(node);
NodeIndex parent = node;
while (parent != class_[parent]) {
CheckNodeBounds(class_[parent]);
CheckNodeBounds(class_[class_[parent]]);
parent = class_[parent];
}
while (node != class_[node]) {
const NodeIndex old_parent = class_[node];
class_[node] = parent;
node = old_parent;
}
return parent;
}
// Returns the equivalence class representative for node.
NodeIndex GetClassRepresentative(NodeIndex node) {
return CompressPath(node);
}
// Returns the number of connected components. Allocates num_nodes_ bits for
// the computation.
NodeIndex GetNumberOfConnectedComponents() {
NodeIndex number = 0;
for (NodeIndex node = 0; node < num_nodes_; ++node) {
if (class_[node] == node) ++number;
}
return number;
}
// Merges the equivalence classes of node1 and node2.
void MergeClasses(NodeIndex node1, NodeIndex node2) {
// It's faster (~10%) to swap the two values and have a single piece of
// code for merging the classes.
CheckNodeBounds(node1);
CheckNodeBounds(node2);
if (class_size_[node1] < class_size_[node2]) {
std::swap(node1, node2);
}
class_[node2] = node1;
class_size_[node1] += class_size_[node2];
}
private:
void CheckNodeBounds(NodeIndex node_index) {
DCHECK_LE(0, node_index);
DCHECK_LT(node_index, num_nodes_);
}
// The exact number of nodes in the graph.
NodeIndex num_nodes_;
// The equivalence class representative for each node.
std::vector<NodeIndex> class_;
// The size of each equivalence class of each node. Used to compress the paths
// and therefore achieve better time complexity.
std::vector<NodeIndex> class_size_;
DISALLOW_COPY_AND_ASSIGN(ConnectedComponents);
};
} // namespace operations_research
#endif // OR_TOOLS_GRAPH_CONNECTIVITY_H_