connectivity.h 5.98 KB
Newer Older
Valentin Platzgummer's avatar
Valentin Platzgummer committed
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
// Copyright 2010-2018 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.

// 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.
// hash_map<int, 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_