LAL: Linear Arrangement Library 24.10.00
A library focused on algorithms on linear arrangements of graphs.
Loading...
Searching...
No Matches
union_find.hpp
1/*********************************************************************
2 *
3 * Linear Arrangement Library - A library that implements a collection
4 * algorithms for linear arrangments of graphs.
5 *
6 * Copyright (C) 2019 - 2024
7 *
8 * This file is part of Linear Arrangement Library. The full code is available
9 * at:
10 * https://github.com/LAL-project/linear-arrangement-library.git
11 *
12 * Linear Arrangement Library is free software: you can redistribute it
13 * and/or modify it under the terms of the GNU Affero General Public License
14 * as published by the Free Software Foundation, either version 3 of the
15 * License, or (at your option) any later version.
16 *
17 * Linear Arrangement Library is distributed in the hope that it will be
18 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Affero General Public License for more details.
21 *
22 * You should have received a copy of the GNU Affero General Public License
23 * along with Linear Arrangement Library. If not, see <http://www.gnu.org/licenses/>.
24 *
25 * Contact:
26 *
27 * LluĂ­s Alemany Puig (lluis.alemany.puig@upc.edu)
28 * LQMC (Quantitative, Mathematical, and Computational Linguisitcs)
29 * CQL (Complexity and Quantitative Linguistics Lab)
30 * Jordi Girona St 1-3, Campus Nord UPC, 08034 Barcelona. CATALONIA, SPAIN
31 * Webpage: https://cqllab.upc.edu/people/lalemany/
32 *
33 * Ramon Ferrer i Cancho (rferrericancho@cs.upc.edu)
34 * LQMC (Quantitative, Mathematical, and Computational Linguisitcs)
35 * CQL (Complexity and Quantitative Linguistics Lab)
36 * Office 220, Omega building
37 * Jordi Girona St 1-3, Campus Nord UPC, 08034 Barcelona. CATALONIA, SPAIN
38 * Webpage: https://cqllab.upc.edu/people/rferrericancho/
39 *
40 ********************************************************************/
41
42#pragma once
43
44// C++ includes
45#if defined DEBUG
46#include <cassert>
47#endif
48
49// lal includes
50#include <lal/basic_types.hpp>
51#include <lal/graphs/rooted_tree.hpp>
52#include <lal/graphs/free_tree.hpp>
53#include <lal/detail/graphs/traversal.hpp>
54
55namespace lal {
56namespace detail {
57
73template <class tree_t>
75(
76 const tree_t& t,
77 const node u,
78 const node v,
79 node * const root_of,
80 uint64_t * const root_size
81)
82noexcept
83{
84 static_assert(
85 std::is_base_of_v<graphs::free_tree, tree_t> or
86 std::is_base_of_v<graphs::rooted_tree, tree_t>
87 );
88
89 // 'u' and 'v' are not connected, so they belong to
90 // (different) connected components of the tree.
91
92 // 'parent' and 'child' determine a direction to be used later.
93 // 'new_root' is the new root for one of the vertices
94 node parent, child, new_root;
95
96 const node root_u = root_of[u];
97 const node root_v = root_of[v];
98
99 const uint64_t size_u = root_size[root_u];
100 const uint64_t size_v = root_size[root_v];
101 const uint64_t new_size = size_u + size_v;
102
103 if (size_u < size_v) {
104 root_of[root_u] = root_v;
105 root_of[u] = root_v;
106
107 new_root = root_v;
108 root_size[new_root] = new_size;
109
110 // update roots in the direction of v -> u
111 parent = v; child = u;
112 }
113 else {
114 root_of[root_v] = root_u;
115 root_of[v] = root_u;
116
117 new_root = root_u;
118 root_size[new_root] = new_size;
119
120 // update roots in the direction of u -> v
121 parent = u; child = v;
122 }
123
124 // update roots of the smaller component,
125 // in the direction parent -> child
126 BFS<tree_t> bfs(t);
127 bfs.set_use_rev_edges( BFS<tree_t>::is_graph_directed );
128 bfs.set_process_current(
129 [&](const BFS<tree_t>&, const node w) -> void { root_of[w] = new_root; }
130 );
131 bfs.set_visited(parent, 1); // avoid going backwards
132 bfs.start_at(child);
133}
134
145template <class tree_t>
147(
148 const tree_t& t,
149 const edge_list& edges,
150 node * const root_of,
151 uint64_t * const root_size
152)
153noexcept
154{
155 static_assert(
156 std::is_base_of_v<graphs::free_tree, tree_t> or
157 std::is_base_of_v<graphs::rooted_tree, tree_t>
158 );
159
160 const auto n = t.get_num_nodes();
161
162 // These two variables are used by the BFS object, but are initialized
163 // in the for loop before 'start_at' is called.
164 uint64_t size_current_root = n + 1;
165 node current_root = n + 1;
166
167 BFS<tree_t> bfs(t);
168 bfs.set_use_rev_edges( BFS<tree_t>::is_graph_directed );
169 bfs.set_process_current(
170 [&](const auto&, const node w) {
171 root_of[w] = current_root;
172 ++size_current_root;
173 }
174 );
175
176 for (const auto& [u, v] : edges) {
177 if (bfs.node_was_visited(u)) { continue; }
178
179 current_root = u;
180 size_current_root = 0;
181 bfs.start_at(u);
182 root_size[current_root] = size_current_root;
183 }
184}
185
196template <class tree_t>
198(
199 const tree_t& t,
200 node * const root_of,
201 uint64_t * const root_size
202)
203noexcept
204{
205 static_assert(
206 std::is_base_of_v<graphs::free_tree, tree_t> or
207 std::is_base_of_v<graphs::rooted_tree, tree_t>
208 );
209
210 const auto n = t.get_num_nodes();
211
212 // These two variables are used by the BFS object, but are initialized
213 // in the for loop before 'start_at' is called.
214 uint64_t size_current_root = n + 1;
215 node current_root = n + 1;
216
217 BFS<tree_t> bfs(t);
218 bfs.set_use_rev_edges( BFS<tree_t>::is_graph_directed );
219 bfs.set_process_current(
220 [&](const auto&, const node w) {
221 root_of[w] = current_root;
222 ++size_current_root;
223 }
224 );
225
226 for (node u = 0; u < n; ++u) {
227 if (bfs.node_was_visited(u)) { continue; }
228
229 current_root = u;
230 size_current_root = 0;
231 bfs.start_at(u);
232 root_size[current_root] = size_current_root;
233 }
234}
235
251template <class tree_t>
253(
254 const tree_t& t,
255 const node u,
256 const node v,
257 node * const root_of,
258 uint64_t * const root_size
259)
260noexcept
261{
262 static_assert(
263 std::is_base_of_v<graphs::free_tree, tree_t> or
264 std::is_base_of_v<graphs::rooted_tree, tree_t>
265 );
266
267// 'u' and 'v' are connected
268#if defined DEBUG
269 assert(root_of[u] == root_of[v]);
270#endif
271
272 const uint64_t size_uv = root_size[root_of[u]];
273
274 BFS<tree_t> bfs(t);
275
276 // --- update u's info ---
277
278 // Update the root of the vertices reachable from 'u'.
279 // (also calculate the size of u's component)
280 uint64_t size_cc_u = 0;
281 bfs.set_use_rev_edges( BFS<tree_t>::is_graph_directed );
282 bfs.set_process_current(
283 [&](const auto&, const node w) -> void {
284 root_of[w] = u;
285 ++size_cc_u;
286 }
287 );
288 bfs.start_at(u);
289 root_of[u] = u;
290 root_size[u] = size_cc_u;
291
292 // --- update v's info ---
293
294 // Update the root of the vertices reachable from 'v'.
295 // (there is no need to reset the BFS object)
296 bfs.set_process_current(
297 [&](const auto&, const node w) -> void {
298 root_of[w] = v;
299 }
300 );
301 bfs.start_at(v);
302 root_of[v] = v;
303 root_size[v] = size_uv - size_cc_u;
304}
305
317template <class tree_t>
319(
320 const tree_t& t,
321 [[maybe_unused]] const edge_list& edges,
322 node * const root_of,
323 uint64_t * const root_size
324)
325noexcept
326{
327 static_assert(
328 std::is_base_of_v<graphs::free_tree, tree_t> or
329 std::is_base_of_v<graphs::rooted_tree, tree_t>
330 );
331
332 const auto n = t.get_num_nodes();
333
334 // These two variables are used by the BFS object, but are initialized
335 // in the for loop before 'start_at' is called.
336 uint64_t size_current_cc = n + 1;
337 node current_root = n + 1;
338
339 BFS<tree_t> bfs(t);
340 bfs.set_use_rev_edges( BFS<tree_t>::is_graph_directed );
341 bfs.set_process_current(
342 [&](const auto&, const node w) {
343 root_of[w] = current_root;
344 ++size_current_cc;
345 }
346 );
347
348 for (const auto& [u, v] : edges) {
349 if (not bfs.node_was_visited(u)) {
350 current_root = u;
351 size_current_cc = 0;
352 bfs.start_at(u);
353 root_size[u] = size_current_cc;
354 }
355
356 if (not bfs.node_was_visited(v)) {
357 current_root = v;
358 size_current_cc = 0;
359 bfs.start_at(v);
360 root_size[v] = size_current_cc;
361 }
362 }
363}
364
365// -----------------------------------------------------------------------------
366
383template <class tree_t>
385(
386 BFS<tree_t>& bfs,
387 const node v,
388 node * const root_of,
389 uint64_t * const root_size
390)
391noexcept
392{
393 static_assert(
394 std::is_base_of_v<graphs::free_tree, tree_t> or
395 std::is_base_of_v<graphs::rooted_tree, tree_t>
396 );
397
398 uint64_t size_cc_v = 0;
399 bfs.set_process_current(
400 [&](const auto&, const node w) -> void {
401 root_of[w] = v;
402 ++size_cc_v;
403 }
404 );
405 bfs.start_at(v);
406
407 root_of[v] = v;
408 root_size[v] = size_cc_v;
409}
410
427template <typename tree_t>
429(
430 const tree_t& t,
431 const node u,
432 node * const root_of,
433 uint64_t * const root_size
434)
435noexcept
436{
437 static_assert(
438 std::is_base_of_v<graphs::free_tree, tree_t> or
439 std::is_base_of_v<graphs::rooted_tree, tree_t>
440 );
441
442 BFS<tree_t> bfs(t);
443 bfs.set_use_rev_edges( BFS<tree_t>::is_graph_directed );
444 // avoid going 'backwards', we need to go 'onwards'
445 bfs.set_visited(u, 1);
446
447 if constexpr (std::is_base_of_v<graphs::free_tree, tree_t>) {
448 for (const node v : t.get_neighbors(u)) {
449 // update size and root of the edges from v onwards
450 // (onwards means "in the direction u -> v"
452 (bfs, v, root_of, root_size);
453 }
454 }
455 else {
456 for (const node v : t.get_in_neighbors(u)) {
457 // update size and root of the edges from v onwards
458 // (onwards means "in the direction u -> v"
460 (bfs, v, root_of, root_size);
461 }
462 for (const node v : t.get_out_neighbors(u)) {
463 // update size and root of the edges from v onwards
464 // (onwards means "in the direction u -> v"
466 (bfs, v, root_of, root_size);
467 }
468 }
469
470 root_of[u] = u;
471 root_size[u] = 1;
472}
473
474} // -- namespace detail
475} // -- namespace lal
Abstract graph Breadth-First Search traversal.
Definition traversal.hpp:89
void update_unionfind_before_remove_edges_incident_to(BFS< tree_t > &bfs, const node v, node *const root_of, uint64_t *const root_size) noexcept
Update Union-Find after the removal of a vertex.
Definition union_find.hpp:385
void update_unionfind_after_add_rem_edges_bulk(const tree_t &t, node *const root_of, uint64_t *const root_size) noexcept
Update the Union-Find data structure after several edges have been operated in bulk.
Definition union_find.hpp:198
void update_unionfind_after_add_edges(const tree_t &t, const edge_list &edges, node *const root_of, uint64_t *const root_size) noexcept
Update the Union-Find data structure after the addition of several edges.
Definition union_find.hpp:147
void update_unionfind_after_remove_edge(const tree_t &t, const node u, const node v, node *const root_of, uint64_t *const root_size) noexcept
Updates Union-Find after the removal of an edge.
Definition union_find.hpp:253
void update_unionfind_after_remove_edges(const tree_t &t, const edge_list &edges, node *const root_of, uint64_t *const root_size) noexcept
Update the Union-Find data structure after the addition of several edges.
Definition union_find.hpp:319
void update_unionfind_after_add_edge(const tree_t &t, const node u, const node v, node *const root_of, uint64_t *const root_size) noexcept
Update the Union-Find data structure after the addition of an edge.
Definition union_find.hpp:75
Main namespace of the library.
Definition basic_types.hpp:48
std::vector< edge > edge_list
See Edge list page for further details.
Definition basic_types.hpp:60
uint64_t node
Node type. See Node / Vertex page for further details.
Definition basic_types.hpp:51