Google OR-Tools v9.15
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-2025 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// The following uses disjoint-sets algorithms, see:
15// https://en.wikipedia.org/wiki/Disjoint-set_data_structure#Disjoint-set_forests
16
18
19#include <algorithm>
20#include <numeric>
21#include <vector>
22
23#include "absl/log/check.h"
25
27 const int old_num_nodes = GetNumberOfNodes();
28 if (num_nodes == old_num_nodes) {
29 return;
30 }
31 CHECK_GE(num_nodes, 0) << "Number of nodes overflowed the `int` type.";
32 CHECK_GT(num_nodes, old_num_nodes);
33 // Each new node starts as an isolated component:
34 // It has itself as root.
35 parent_.resize(num_nodes);
36 std::iota(parent_.begin() + old_num_nodes, parent_.end(), old_num_nodes);
37 // It's in an isolated component of size 1.
38 component_size_.resize(num_nodes, 1);
39 // Its rank is 0.
40 rank_.resize(num_nodes);
41 // This introduces one extra component per added node.
42 num_components_ += num_nodes - old_num_nodes;
43}
44
46 DCHECK_GE(node, 0);
47 DCHECK_LT(node, GetNumberOfNodes());
48
49 // Search the root.
50 int root = parent_[node];
51 while (parent_[root] != root) {
52 root = parent_[root];
53 }
54
55 // Apply path compression.
56 while (node != root) {
57 const int prev_parent = parent_[node];
58 parent_[node] = root;
59 node = prev_parent;
60 }
61 return root;
62}
63
65 const int num_nodes = GetNumberOfNodes();
66 if (num_nodes != num_nodes_at_last_get_roots_call_) {
67 // Add potential roots for each new node that did not exist the last time
68 // GetComponentRoots() was called. The cost here is amortized against
69 // adding the nodes in the first place.
70 const int previous_num_roots = roots_.size();
71 roots_.resize(previous_num_roots + num_nodes -
72 num_nodes_at_last_get_roots_call_);
73 std::iota(roots_.begin() + previous_num_roots, roots_.end(),
74 num_nodes_at_last_get_roots_call_);
75 }
76
77 // Remove the roots that have been merged with other components. Each node
78 // only gets removed once from the roots vector, so the cost of FindRoot() is
79 // amortized against adding the edge.
81 &roots_, [&](const int node) { return node != FindRoot(node); });
82
83 num_nodes_at_last_get_roots_call_ = num_nodes;
84 return roots_;
85}
86
87bool DenseConnectedComponentsFinder::AddEdge(int node1, int node2) {
88 // Grow if needed.
89 const int min_num_nodes = std::max(node1, node2) + 1;
90 if (min_num_nodes > GetNumberOfNodes()) {
91 SetNumberOfNodes(min_num_nodes);
92 }
93
94 // Just union the sets for node1 and node2.
95 int root1 = FindRoot(node1);
96 int root2 = FindRoot(node2);
97
98 // Already the same set.
99 if (root1 == root2) {
100 return false;
101 }
102
103 DCHECK_GE(num_components_, 2);
104 --num_components_;
105
106 const int component_size = component_size_[root1] + component_size_[root2];
107
108 // Attach the shallowest tree to root of the deepest one. Note that this
109 // operation grows the rank of the new common root by at most one (if the two
110 // trees originally have the same rank).
111 if (rank_[root1] > rank_[root2]) {
112 parent_[root2] = root1;
113 component_size_[root1] = component_size;
114 } else {
115 parent_[root1] = root2;
116 component_size_[root2] = component_size;
117 // If the ranks were the same then attaching just grew the rank by one.
118 if (rank_[root1] == rank_[root2]) {
119 ++rank_[root2];
120 }
121 }
122 return true;
123}
124
126 if (node1 < 0 || node1 >= GetNumberOfNodes() || node2 < 0 ||
127 node2 >= GetNumberOfNodes()) {
128 return false;
129 }
130 return FindRoot(node1) == FindRoot(node2);
131}
132
134 if (node < 0 || node >= GetNumberOfNodes()) {
135 return 0;
136 }
137 return component_size_[FindRoot(node)];
138}
139
141 std::vector<int> component_ids(GetNumberOfNodes(), -1);
142 int current_component = 0;
143 for (int node = 0; node < GetNumberOfNodes(); ++node) {
144 int& root_component = component_ids[FindRoot(node)];
145 if (root_component < 0) {
146 // This is the first node in a yet unseen component.
147 root_component = current_component;
148 ++current_component;
149 }
150 component_ids[node] = root_component;
151 }
152 return component_ids;
153}
bool Connected(int node1, int node2)
bool AddEdge(int node1, int node2)
const std::vector< int > & GetComponentRoots()
void STLEraseAllFromSequenceIf(T *v, P pred)
Definition stl_util.h:104