Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
connected_components.cc
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//
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
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
98bool 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
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}
bool Connected(int node1, int node2)
bool AddEdge(int node1, int node2)
std::vector< int > GetComponentIds()
Returns the same as GetConnectedComponents().
const std::vector< int > & GetComponentRoots()
void STLEraseAllFromSequenceIf(T *v, P pred)
Remove each element e in v satisfying pred(e).
Definition stl_util.h:107