OR-Tools  9.6
connected_components.cc
Go to the documentation of this file.
1 // Copyright 2010-2022 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 //
15 // Licensed under the Apache License, Version 2.0 (the "License");
16 // you may not use this file except in compliance with the License.
17 // You may obtain a copy of the License at
18 //
19 // http://www.apache.org/licenses/LICENSE-2.0
20 //
21 // Unless required by applicable law or agreed to in writing, software
22 // distributed under the License is distributed on an "AS IS" BASIS,
23 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24 // See the License for the specific language governing permissions and
25 // limitations under the License.
26 
27 // The following uses disjoint-sets algorithms, see:
28 // https://en.wikipedia.org/wiki/Disjoint-set_data_structure#Disjoint-set_forests
29 
31 
32 #include <algorithm>
33 #include <numeric>
34 #include <vector>
35 
36 #include "ortools/base/stl_util.h"
37 
39  const int old_num_nodes = GetNumberOfNodes();
40  if (num_nodes == old_num_nodes) {
41  return;
42  }
43  CHECK_GT(num_nodes, old_num_nodes);
44  // Each new node starts as an isolated component:
45  // It has itself as root.
46  parent_.resize(num_nodes);
47  std::iota(parent_.begin() + old_num_nodes, parent_.end(), old_num_nodes);
48  // It's in an isolated component of size 1.
49  component_size_.resize(num_nodes, 1);
50  // Its rank is 0.
51  rank_.resize(num_nodes);
52  // This introduces one extra component per added node.
53  num_components_ += num_nodes - old_num_nodes;
54 }
55 
57  DCHECK_GE(node, 0);
58  DCHECK_LT(node, GetNumberOfNodes());
59 
60  // Search the root.
61  int root = parent_[node];
62  while (parent_[root] != root) {
63  root = parent_[root];
64  }
65 
66  // Apply path compression.
67  while (node != root) {
68  const int prev_parent = parent_[node];
69  parent_[node] = root;
70  node = prev_parent;
71  }
72  return root;
73 }
74 
76  const int num_nodes = GetNumberOfNodes();
77  if (num_nodes != num_nodes_at_last_get_roots_call_) {
78  // Add potential roots for each new node that did not exist the last time
79  // GetComponentRoots() was called. The cost here is amortized against
80  // adding the nodes in the first place.
81  const int previous_num_roots = roots_.size();
82  roots_.resize(previous_num_roots + num_nodes -
83  num_nodes_at_last_get_roots_call_);
84  std::iota(roots_.begin() + previous_num_roots, roots_.end(),
85  num_nodes_at_last_get_roots_call_);
86  }
87 
88  // Remove the roots that have been merged with other components. Each node
89  // only gets removed once from the roots vector, so the cost of FindRoot() is
90  // amortized against adding the edge.
92  &roots_, [&](const int node) { return node != FindRoot(node); });
93 
94  num_nodes_at_last_get_roots_call_ = num_nodes;
95  return roots_;
96 }
97 
98 bool DenseConnectedComponentsFinder::AddEdge(int node1, int node2) {
99  // Grow if needed.
100  const int min_num_nodes = std::max(node1, node2) + 1;
101  if (min_num_nodes > GetNumberOfNodes()) {
102  SetNumberOfNodes(min_num_nodes);
103  }
104 
105  // Just union the sets for node1 and node2.
106  int root1 = FindRoot(node1);
107  int root2 = FindRoot(node2);
108 
109  // Already the same set.
110  if (root1 == root2) {
111  return false;
112  }
113 
114  DCHECK_GE(num_components_, 2);
115  --num_components_;
116 
117  const int component_size = component_size_[root1] + component_size_[root2];
118 
119  // Attach the shallowest tree to root of the deepest one. Note that this
120  // operation grows the rank of the new common root by at most one (if the two
121  // trees originally have the same rank).
122  if (rank_[root1] > rank_[root2]) {
123  parent_[root2] = root1;
124  component_size_[root1] = component_size;
125  } else {
126  parent_[root1] = root2;
127  component_size_[root2] = component_size;
128  // If the ranks were the same then attaching just grew the rank by one.
129  if (rank_[root1] == rank_[root2]) {
130  ++rank_[root2];
131  }
132  }
133  return true;
134 }
135 
136 bool DenseConnectedComponentsFinder::Connected(int node1, int node2) {
137  if (node1 < 0 || node1 >= GetNumberOfNodes() || node2 < 0 ||
138  node2 >= GetNumberOfNodes()) {
139  return false;
140  }
141  return FindRoot(node1) == FindRoot(node2);
142 }
143 
145  if (node < 0 || node >= GetNumberOfNodes()) {
146  return 0;
147  }
148  return component_size_[FindRoot(node)];
149 }
150 
152  std::vector<int> component_ids(GetNumberOfNodes(), -1);
153  int current_component = 0;
154  for (int node = 0; node < GetNumberOfNodes(); ++node) {
155  int& root_component = component_ids[FindRoot(node)];
156  if (root_component < 0) {
157  // This is the first node in a yet unseen component.
158  root_component = current_component;
159  ++current_component;
160  }
161  component_ids[node] = root_component;
162  }
163  return component_ids;
164 }
int64_t max
Definition: alldiff_cst.cc:140
bool AddEdge(int node1, int node2)
bool Connected(int node1, int node2)
const std::vector< int > & GetComponentRoots()
void STLEraseAllFromSequenceIf(T *v, P pred)
Definition: stl_util.h:107