Open
Graph Drawing
Framework

 v. 2023.09 (Elderberry)
 

DisjointSets.h
Go to the documentation of this file.
1 
32 #pragma once
33 
34 #include <ogdf/basic/exceptions.h>
36 
37 #include <cstring>
38 
39 namespace ogdf {
40 
41 #define OGDF_DISJOINT_SETS_INTERMEDIATE_PARENT_CHECK
42 
44 enum class LinkOptions {
45  Naive = 0,
46  Index = 1,
47  Size = 2,
48  Rank = 3
49 };
50 
52 enum class CompressionOptions {
53  PathCompression = 0,
54  PathSplitting = 1,
55  PathHalving = 2,
56  Type1Reversal = 4,
57  Collapsing = 5,
58  Disabled = 6
59 };
60 
62 enum class InterleavingOptions {
63  Disabled = 0,
64  Rem = 1,
65  Tarjan = 2,
66  Type0Reversal = 3,
68  4
69 };
70 
71 namespace disjoint_sets {
72 
73 struct AnyOption { };
74 
75 template<LinkOptions linkOption>
76 struct LinkOption : AnyOption { };
77 
78 template<CompressionOptions compressionOption>
80 
81 template<InterleavingOptions interleavingOption>
83 
84 }
85 
87 template<LinkOptions linkOption = LinkOptions::Index,
90 class DisjointSets {
91  static_assert(interleavingOption != InterleavingOptions::Rem || linkOption == LinkOptions::Index,
92  "Rem's Algorithm requires linking by index.");
93  static_assert(interleavingOption != InterleavingOptions::Tarjan || linkOption == LinkOptions::Rank,
94  "Tarjan and van Leeuwen's Algorithm requires linking by rank.");
95  static_assert(interleavingOption != InterleavingOptions::Type0Reversal
96  || linkOption == LinkOptions::Naive,
97  "Interleaved Reversal Type 0 requires naïve linking.");
98  static_assert(interleavingOption != InterleavingOptions::SplittingCompression
99  || linkOption == LinkOptions::Index,
100  "Interleaved Path Splitting Path Compression requires linking by index.");
101 
102 private:
103  int m_numberOfSets;
106 
107  // Arrays parents, elements, parameters, siblings map a set id to its properties.
108 
109  int* m_parents;
111  int* m_siblings;
112 
113  //find
120 
121  //link
122  int link(disjoint_sets::LinkOption<LinkOptions::Naive>, int set1, int set2);
123  int link(disjoint_sets::LinkOption<LinkOptions::Index>, int set1, int set2);
124  int link(disjoint_sets::LinkOption<LinkOptions::Size>, int set1, int set2);
125  int link(disjoint_sets::LinkOption<LinkOptions::Rank>, int set1, int set2);
126 
127  //quickUnion
132  int set2);
139  int set2);
140 
141 public:
143 
146  explicit DisjointSets(int maxNumberOfElements = (1 << 15))
147  : m_parents(nullptr), m_parameters(nullptr), m_siblings(nullptr) {
148  init(maxNumberOfElements);
149  }
150 
152  delete[] this->m_parents;
153  delete[] this->m_parameters;
154  delete[] this->m_siblings;
155  }
156 
158  m_numberOfSets = copy.m_numberOfSets;
159  m_numberOfElements = copy.m_numberOfElements;
160  if (copy.m_parents) {
161  std::copy_n(copy.m_parents, m_maxNumberOfElements, m_parents);
162  }
163  if (copy.m_parameters) {
164  std::copy_n(copy.m_parameters, m_maxNumberOfElements, m_parameters);
165  }
166  if (copy.m_siblings) {
167  std::copy_n(copy.m_siblings, m_maxNumberOfElements, m_siblings);
168  }
169  }
170 
172  std::swap(first.m_numberOfSets, second.m_numberOfSets);
173  std::swap(first.m_numberOfElements, second.m_numberOfElements);
174  std::swap(first.m_maxNumberOfElements, second.m_maxNumberOfElements);
175  std::swap(first.m_parents, second.m_parents);
176  std::swap(first.m_parameters, second.m_parameters);
177  std::swap(first.m_siblings, second.m_siblings);
178  };
179 
181 
182 
183  void init(int maxNumberOfElements) {
184  this->m_maxNumberOfElements = maxNumberOfElements;
185  init();
186  }
187 
189  void init() {
190  delete[] this->m_parents;
191  delete[] this->m_parameters;
192  delete[] this->m_siblings;
193  this->m_numberOfSets = 0;
194  this->m_numberOfElements = 0;
195  this->m_parents = new int[this->m_maxNumberOfElements];
196  this->m_parameters = (linkOption == LinkOptions::Rank || linkOption == LinkOptions::Size)
197  ? new int[this->m_maxNumberOfElements]
198  : nullptr;
199  this->m_siblings = (compressionOption == CompressionOptions::Collapsing)
200  ? new int[this->m_maxNumberOfElements]
201  : nullptr;
202  }
203 
205 
210  int find(int set) {
211  OGDF_ASSERT(set >= 0);
214  }
215 
217 
222  int getRepresentative(int set) const {
223  OGDF_ASSERT(set >= 0);
225  while (set != m_parents[set]) {
226  set = m_parents[set];
227  }
228  return set;
229  }
230 
232 
235  int makeSet() {
236  if (this->m_numberOfElements == this->m_maxNumberOfElements) {
237  int* parents = this->m_parents;
238  this->m_parents = new int[this->m_maxNumberOfElements * 2];
239  memcpy(this->m_parents, parents, sizeof(int) * this->m_maxNumberOfElements);
240  delete[] parents;
241 
242  if (this->m_parameters != nullptr) {
243  int* parameters = this->m_parameters;
244  this->m_parameters = new int[this->m_maxNumberOfElements * 2];
245  memcpy(this->m_parameters, parameters, sizeof(int) * this->m_maxNumberOfElements);
246  delete[] parameters;
247  }
248 
249  if (this->m_siblings != nullptr) {
250  int* siblings = this->m_siblings;
251  this->m_siblings = new int[this->m_maxNumberOfElements * 2];
252  memcpy(this->m_siblings, siblings, sizeof(int) * this->m_maxNumberOfElements);
253  delete[] siblings;
254  }
255  this->m_maxNumberOfElements *= 2;
256  }
257  this->m_numberOfSets++;
258  int id = this->m_numberOfElements++;
259  this->m_parents[id] = id;
260  //Initialize size/ rank/ sibling.
261  if (linkOption == LinkOptions::Size) {
262  this->m_parameters[id] = 1;
263  } else if (linkOption == LinkOptions::Rank) {
264  this->m_parameters[id] = 0;
265  }
266  if (compressionOption == CompressionOptions::Collapsing) {
267  this->m_siblings[id] = -1;
268  }
269  return id;
270  }
271 
273 
277  int link(int set1, int set2) {
278  OGDF_ASSERT(set1 == getRepresentative(set1));
279  OGDF_ASSERT(set2 == getRepresentative(set2));
280  if (set1 == set2) {
281  return -1;
282  }
283  this->m_numberOfSets--;
284  return linkPure(set1, set2);
285  }
286 
288 
291  bool quickUnion(int set1, int set2) {
292  if (set1 == set2) {
293  return false;
294  }
297  m_numberOfSets -= result;
298  return result;
299  }
300 
302  int getNumberOfSets() { return m_numberOfSets; }
303 
306 
307 private:
309 
313  int linkPure(int set1, int set2) {
314  int superset = link(disjoint_sets::LinkOption<linkOption>(), set1, set2);
315  //Collapse subset tree.
316  if (compressionOption == CompressionOptions::Collapsing) {
317  int subset = set1 == superset ? set2 : set1;
318  int id = subset;
319  while (this->m_siblings[id] != -1) {
320  id = this->m_siblings[id];
321  this->m_parents[id] = superset;
322  }
323  this->m_siblings[id] = this->m_siblings[superset];
324  this->m_siblings[superset] = subset;
325  }
326  return superset;
327  }
328 };
329 
330 //find
331 template<LinkOptions linkOption, CompressionOptions compressionOption, InterleavingOptions interleavingOption>
334  int parent = m_parents[set];
335  if (set == parent) {
336  return set;
337  } else {
339  parent);
340  m_parents[set] = parent;
341  return parent;
342  }
343 }
344 
345 template<LinkOptions linkOption, CompressionOptions compressionOption, InterleavingOptions interleavingOption>
348  while (set != m_parents[set]) {
349  int parent = m_parents[set];
350  int grandParent = m_parents[parent];
351  m_parents[set] = grandParent;
352  set = grandParent;
353  }
354  return set;
355 }
356 
357 template<LinkOptions linkOption, CompressionOptions compressionOption, InterleavingOptions interleavingOption>
360  int parent = m_parents[set];
361  int grandParent = m_parents[parent];
362  while (parent != grandParent) {
363  m_parents[set] = grandParent;
364  set = parent;
365  parent = grandParent;
366  grandParent = m_parents[grandParent];
367  }
368  return parent;
369 }
370 
371 template<LinkOptions linkOption, CompressionOptions compressionOption, InterleavingOptions interleavingOption>
374  int root = set;
375  set = m_parents[root];
376 
377  while (set != m_parents[set]) {
378  int parent = m_parents[set];
379  m_parents[set] = root;
380  set = parent;
381  }
382  m_parents[root] = set;
383  return set;
384 }
385 
386 template<LinkOptions linkOption, CompressionOptions compressionOption, InterleavingOptions interleavingOption>
389  while (set != m_parents[set]) {
390  set = m_parents[set];
391  }
392  return set;
393 }
394 
395 template<LinkOptions linkOption, CompressionOptions compressionOption, InterleavingOptions interleavingOption>
398  return m_parents[set];
399 }
400 
401 //quickUnion
402 template<LinkOptions linkOption, CompressionOptions compressionOption, InterleavingOptions interleavingOption>
405  int set1, int set2) {
406 #ifdef OGDF_DISJOINT_SETS_INTERMEDIATE_PARENT_CHECK
407  if (m_parents[set1] == m_parents[set2]) {
408  return false;
409  }
410 #endif
411  set1 = find(set1);
412  set2 = find(set2);
413  if (set1 != set2) {
414  linkPure(set1, set2);
415  return true;
416  }
417  return false;
418 }
419 
420 template<LinkOptions linkOption, CompressionOptions compressionOption, InterleavingOptions interleavingOption>
424 #ifdef OGDF_DISJOINT_SETS_INTERMEDIATE_PARENT_CHECK
425  if (m_parents[set1] == m_parents[set2]) {
426  return false;
427  }
428 #endif
429  int root = set2;
430  int set = set2;
431  int parent = m_parents[set];
432  m_parents[set] = root;
433  while (set != parent) {
434  if (parent == set1) {
435  m_parents[root] = set1;
436  return false;
437  }
438  set = parent;
439  parent = m_parents[set];
440  m_parents[set] = root;
441  }
442 
443  set = set1;
444  parent = m_parents[set];
445  while (true) {
446  if (parent == root) {
447  return false;
448  }
449  m_parents[set] = root;
450  if (parent == set) {
451  return true;
452  }
453  set = parent;
454  parent = m_parents[set];
455  }
456 }
457 
458 template<LinkOptions linkOption, CompressionOptions compressionOption, InterleavingOptions interleavingOption>
462  int r_x = set1;
463  int r_y = set2;
464  int p_r_x = m_parents[r_x];
465  int p_r_y = m_parents[r_y];
466  while (p_r_x != p_r_y) {
467  if (p_r_x < p_r_y) {
468  if (r_x == p_r_x) {
469  m_parents[r_x] = p_r_y;
470  return true;
471  }
472  m_parents[r_x] = p_r_y;
473  r_x = p_r_x;
474  p_r_x = m_parents[r_x];
475  } else {
476  if (r_y == p_r_y) {
477  m_parents[r_y] = p_r_x;
478  return true;
479  }
480  m_parents[r_y] = p_r_x;
481  r_y = p_r_y;
482  p_r_y = m_parents[r_y];
483  }
484  }
485  return false;
486 }
487 
488 template<LinkOptions linkOption, CompressionOptions compressionOption, InterleavingOptions interleavingOption>
492  int set2) {
493 #ifdef OGDF_DISJOINT_SETS_INTERMEDIATE_PARENT_CHECK
494  if (m_parents[set1] == m_parents[set2]) {
495  return false;
496  }
497 #endif
498  int set = set1;
499 
500  if (set1 < set2) {
501  set = set2;
502  set2 = set1;
503  set1 = set;
504  }
505 
507  set = m_parents[set];
508  int parent = m_parents[set];
509  int grandParent = m_parents[parent];
510  while (parent != grandParent) {
511  m_parents[set] = grandParent;
512  set = parent;
513  parent = grandParent;
514  grandParent = m_parents[grandParent];
515  }
516  m_parents[set1] = parent;
517  int root = parent;
518 
520  set = set2;
521  parent = m_parents[set];
522  while (true) {
523  if (parent < root) {
524  m_parents[set] = root;
525  if (set == parent) {
526  return true;
527  }
528  set = parent;
529  parent = m_parents[set];
530  } else if (parent > root) {
531  m_parents[root] = parent;
532  m_parents[set1] = parent;
533  m_parents[set2] = parent;
534  return true;
535  } else {
536  return false;
537  }
538  }
539 }
540 
541 template<LinkOptions linkOption, CompressionOptions compressionOption, InterleavingOptions interleavingOption>
545  int r_x = set1;
546  int r_y = set2;
547  int p_r_x = m_parents[r_x];
548  int p_r_y = m_parents[r_y];
549  while (p_r_x != p_r_y) {
550  if (m_parameters[p_r_x] <= m_parameters[p_r_y]) {
551  if (r_x == p_r_x) {
552  if (m_parameters[p_r_x] == m_parameters[p_r_y] && p_r_y == m_parents[p_r_y]) {
553  m_parameters[p_r_y]++;
554  }
555  m_parents[r_x] = m_parents[p_r_y];
556  return true;
557  }
558  m_parents[r_x] = p_r_y;
559  r_x = p_r_x;
560  p_r_x = m_parents[r_x];
561  } else {
562  if (r_y == p_r_y) {
563  m_parents[r_y] = m_parents[p_r_x];
564  return true;
565  }
566  m_parents[r_y] = p_r_x;
567  r_y = p_r_y;
568  p_r_y = m_parents[r_y];
569  }
570  }
571  return false;
572 }
573 
574 //link
575 template<LinkOptions linkOption, CompressionOptions compressionOption, InterleavingOptions interleavingOption>
577  disjoint_sets::LinkOption<LinkOptions::Index>, int set1, int set2) {
578  if (set1 < set2) {
579  m_parents[set1] = set2;
580  return set2;
581  } else {
582  m_parents[set2] = set1;
583  return set1;
584  }
585 }
586 
587 template<LinkOptions linkOption, CompressionOptions compressionOption, InterleavingOptions interleavingOption>
589  disjoint_sets::LinkOption<LinkOptions::Rank>, int set1, int set2) {
590  int parameter1 = m_parameters[set1];
591  int parameter2 = m_parameters[set2];
592 
593  if (parameter1 < parameter2) {
594  m_parents[set1] = set2;
595  return set2;
596  } else if (parameter1 > parameter2) {
597  m_parents[set2] = set1;
598  return set1;
599  } else {
600  m_parents[set1] = set2;
601  m_parameters[set2]++;
602  return set2;
603  }
604 }
605 
606 template<LinkOptions linkOption, CompressionOptions compressionOption, InterleavingOptions interleavingOption>
608  disjoint_sets::LinkOption<LinkOptions::Size>, int set1, int set2) {
609  int parameter1 = m_parameters[set1];
610  int parameter2 = m_parameters[set2];
611 
612  if (parameter1 < parameter2) {
613  m_parents[set1] = set2;
614  m_parameters[set2] += parameter1;
615  return set2;
616  } else {
617  m_parents[set2] = set1;
618  m_parameters[set1] += parameter2;
619  return set1;
620  }
621 }
622 
623 template<LinkOptions linkOption, CompressionOptions compressionOption, InterleavingOptions interleavingOption>
625  disjoint_sets::LinkOption<LinkOptions::Naive>, int set1, int set2) {
626  m_parents[set1] = set2;
627  return set2;
628 }
629 
630 }
OGDF_COPY_CONSTR
#define OGDF_COPY_CONSTR(cls)
Declares the copy constructor for class cls.
Definition: copy_move.h:57
ogdf
The namespace for all OGDF objects.
Definition: AugmentationModule.h:36
ogdf::DisjointSets::m_numberOfElements
int m_numberOfElements
Current number of elements.
Definition: DisjointSets.h:104
exceptions.h
Definition of exception classes.
ogdf::DisjointSets::~DisjointSets
~DisjointSets()
Definition: DisjointSets.h:151
OGDF_ASSERT
#define OGDF_ASSERT(expr)
Assert condition expr. See doc/build.md for more information.
Definition: basic.h:54
ogdf::DisjointSets::m_maxNumberOfElements
int m_maxNumberOfElements
Maximum number of elements (array size) adjusted dynamically.
Definition: DisjointSets.h:105
copy_move.h
Utility macros for declaring copy and move constructors and assignment operations.
ogdf::DisjointSets::find
int find(disjoint_sets::CompressionOption< CompressionOptions::PathCompression >, int set)
Definition: DisjointSets.h:332
ogdf::InterleavingOptions::Type0Reversal
@ Type0Reversal
Interleaved Reversal of Type 0 (only compatible with LinkOptions::Naive)
ogdf::disjoint_sets::AnyOption
Definition: DisjointSets.h:73
ogdf::disjoint_sets::CompressionOption
Definition: DisjointSets.h:79
ogdf::CompressionOptions::PathSplitting
@ PathSplitting
Path Splitting (default)
ogdf::DisjointSets::makeSet
int makeSet()
Initializes a singleton set.
Definition: DisjointSets.h:235
ogdf::DisjointSets::m_parameters
int * m_parameters
Maps set id to rank/size.
Definition: DisjointSets.h:110
ogdf::LinkOptions::Naive
@ Naive
Naive Link.
ogdf::InterleavingOptions::Disabled
@ Disabled
No Interleaving (default)
ogdf::InterleavingOptions::Tarjan
@ Tarjan
Tarjan's and van Leeuwen's Algorithm (only compatible with LinkOptions::Rank)
ogdf::InterleavingOptions::Rem
@ Rem
Rem's Algorithm (only compatible with LinkOptions::Index)
ogdf::DisjointSets::init
void init()
Resets the DisjointSets structure to be empty, preserving the previous value of maxNumberOfElements.
Definition: DisjointSets.h:189
ogdf::DisjointSets::link
int link(disjoint_sets::LinkOption< LinkOptions::Naive >, int set1, int set2)
Definition: DisjointSets.h:624
ogdf::LinkOptions::Rank
@ Rank
Link by rank.
Minisat::Internal::copy
static void copy(const T &from, T &to)
Definition: Alg.h:61
ogdf::DisjointSets
A Union/Find data structure for maintaining disjoint sets.
Definition: DisjointSets.h:90
ogdf::DisjointSets::m_siblings
int * m_siblings
Maps set id to sibling set id.
Definition: DisjointSets.h:111
ogdf::DisjointSets::getNumberOfSets
int getNumberOfSets()
Returns the current number of disjoint sets.
Definition: DisjointSets.h:302
ogdf::LinkOptions
LinkOptions
Defines options for linking two sets.
Definition: DisjointSets.h:44
ogdf::DisjointSets::getNumberOfElements
int getNumberOfElements()
Returns the current number of elements.
Definition: DisjointSets.h:305
ogdf::DisjointSets::quickUnion
bool quickUnion(int set1, int set2)
Unions the maximal disjoint sets containing set1 and set2.
Definition: DisjointSets.h:291
ogdf::DisjointSets::find
int find(int set)
Returns the id of the largest superset of set and compresses the path according to CompressionOptions...
Definition: DisjointSets.h:210
ogdf::CompressionOptions::PathCompression
@ PathCompression
Path Compression.
ogdf::InterleavingOptions
InterleavingOptions
Defines options for interleaving find/link operations in quickUnion.
Definition: DisjointSets.h:62
ogdf::LinkOptions::Index
@ Index
Link by index (default)
ogdf::CompressionOptions::Type1Reversal
@ Type1Reversal
Reversal of type 1.
ogdf::DisjointSets::DisjointSets
DisjointSets(int maxNumberOfElements=(1<< 15))
Creates an empty DisjointSets structure.
Definition: DisjointSets.h:146
OGDF_COPY_MOVE_BY_SWAP
#define OGDF_COPY_MOVE_BY_SWAP(cls)
Provide move construct/assign and copy assign given there is a copy constructor and swap.
Definition: copy_move.h:76
ogdf::disjoint_sets::InterleavingOption
Definition: DisjointSets.h:82
ogdf::DisjointSets::quickUnion
bool quickUnion(disjoint_sets::LinkOption< LinkOptions::Index >, disjoint_sets::InterleavingOption< InterleavingOptions::Rem >, int set1, int set2)
Definition: DisjointSets.h:459
ogdf::DisjointSets::m_parents
int * m_parents
Maps set id to parent set id.
Definition: DisjointSets.h:109
ogdf::DisjointSets::link
int link(int set1, int set2)
Unions set1 and set2.
Definition: DisjointSets.h:277
ogdf::CompressionOptions
CompressionOptions
Defines options for compression search paths.
Definition: DisjointSets.h:52
ogdf::LinkOptions::Size
@ Size
Link by size.
ogdf::CompressionOptions::PathHalving
@ PathHalving
Path Halving.
ogdf::CompressionOptions::Disabled
@ Disabled
No Compression.
ogdf::DisjointSets::getRepresentative
int getRepresentative(int set) const
Returns the id of the largest superset of set.
Definition: DisjointSets.h:222
ogdf::CompressionOptions::Collapsing
@ Collapsing
Collapsing.
ogdf::DisjointSets::m_numberOfSets
int m_numberOfSets
Current number of disjoint sets.
Definition: DisjointSets.h:92
ogdf::InterleavingOptions::SplittingCompression
@ SplittingCompression
Interleaved Path Splitting Path Compression (only compatible with LinkOptions::Index)
ogdf::DisjointSets::linkPure
int linkPure(int set1, int set2)
Unions set1 and set2 w/o decreasing the numberOfSets.
Definition: DisjointSets.h:313
Minisat::Internal::find
static bool find(V &ts, const T &t)
Definition: Alg.h:47
OGDF_SWAP_OP
#define OGDF_SWAP_OP(cls)
Declares the swap function for class cls.
Definition: copy_move.h:65