Jlm
disjointset.hpp
Go to the documentation of this file.
1 /*
2  * Copyright 2020 Nico Reißmann <nico.reissmann@gmail.com>
3  * See COPYING for terms of redistribution.
4  */
5 
6 #ifndef JLM_UTIL_DISJOINTSET_HPP
7 #define JLM_UTIL_DISJOINTSET_HPP
8 
9 #include <jlm/util/common.hpp>
10 
11 #include <memory>
12 #include <unordered_map>
13 #include <unordered_set>
14 #include <vector>
15 
16 namespace jlm::util
17 {
18 
19 template<class T>
20 class DisjointSet final
21 {
22 public:
23  class Set;
24 
25 private:
26  class MemberIterator final
27  {
28  public:
29  using iterator_category = std::forward_iterator_tag;
30  using value_type = const T;
31  using difference_type = std::ptrdiff_t;
32  using pointer = const T *;
33  using reference = const T &;
34 
35  private:
36  friend class DisjointSet::Set;
37 
38  explicit MemberIterator(const Set * node)
39  : node_(node)
40  {}
41 
42  public:
43  reference
44  operator*() const
45  {
46  JLM_ASSERT(node_ != nullptr);
47  return node_->value();
48  }
49 
50  pointer
51  operator->() const
52  {
53  return &operator*();
54  }
55 
58  {
59  JLM_ASSERT(node_ != nullptr);
60 
61  node_ = node_->is_root() ? nullptr : node_->next_;
62  return *this;
63  }
64 
67  {
68  MemberIterator tmp = *this;
69  ++*this;
70  return tmp;
71  }
72 
73  bool
74  operator==(const MemberIterator & other) const
75  {
76  return node_ == other.node_;
77  }
78 
79  bool
80  operator!=(const MemberIterator & other) const
81  {
82  return !operator==(other);
83  }
84 
85  private:
86  const Set * node_;
87  };
88 
89 public:
90  class Set final
91  {
92  friend class DisjointSet;
93 
94  private:
95  Set(const T & value)
96  : value_(value),
97  size_(1),
98  next_(this),
99  parent_(this)
100  {}
101 
102  Set(const Set &) = delete;
103 
104  Set(Set && other) = delete;
105 
106  Set &
107  operator=(const Set &) = delete;
108 
109  Set &
110  operator=(Set && other) = delete;
111 
112  static std::unique_ptr<Set>
113  create(const T & value)
114  {
115  return std::unique_ptr<Set>(new Set(value));
116  }
117 
118  const Set *
119  root() const noexcept
120  {
121  auto child = this;
122  auto parent = parent_;
123 
124  /* path halving */
125  while (parent != child)
126  {
127  parent_ = parent->parent_;
128  child = parent;
129  parent = parent_;
130  }
131 
132  return child;
133  }
134 
135  public:
136  bool
137  operator==(const Set & other) const noexcept
138  {
139  return value_ == other.value_;
140  }
141 
142  bool
143  operator!=(const Set & other) const noexcept
144  {
145  return !operator==(other);
146  }
147 
149  begin() const
150  {
151  return MemberIterator(root()->next_);
152  }
153 
155  end() const
156  {
157  return MemberIterator(nullptr);
158  }
159 
160  size_t
161  nmembers() const noexcept
162  {
163  return root()->size_;
164  }
165 
166  const T &
167  value() const noexcept
168  {
169  return value_;
170  }
171 
172  bool
173  is_root() const noexcept
174  {
175  return this == parent_;
176  }
177 
178  private:
180  mutable size_t size_;
181  mutable const Set * next_;
182  mutable const Set * parent_;
183  };
184 
185  class SetIterator final
186  {
187  public:
188  using iterator_category = std::forward_iterator_tag;
189  using value_type = const Set *;
190  using difference_type = ptrdiff_t;
191  using pointer = const Set **;
192  using reference = const Set *&;
193 
194  private:
195  friend class DisjointSet;
196 
197  SetIterator(const typename std::unordered_set<const Set *>::const_iterator & it)
198  : it_(it)
199  {}
200 
201  public:
202  const Set &
203  operator*() const
204  {
205  JLM_ASSERT(*it_ != nullptr);
206  return **it_;
207  }
208 
209  const Set *
210  operator->() const
211  {
212  return &operator*();
213  }
214 
215  SetIterator &
217  {
218  ++it_;
219  return *this;
220  }
221 
224  {
225  MemberIterator tmp = *this;
226  ++*this;
227  return tmp;
228  }
229 
230  bool
231  operator==(const SetIterator & other) const
232  {
233  return it_ == other.it_;
234  }
235 
236  bool
237  operator!=(const SetIterator & other) const
238  {
239  return !operator==(other);
240  }
241 
242  private:
243  typename std::unordered_set<const Set *>::const_iterator it_;
244  };
245 
246 public:
247  constexpr DisjointSet() = default;
248 
249  explicit DisjointSet(const std::vector<T> & elements)
250  {
251  insert(elements);
252  }
253 
254  DisjointSet(const DisjointSet & other)
255  {
256  operator=(other);
257  }
258 
260  {
261  operator=(other);
262  }
263 
264  DisjointSet &
265  operator=(const DisjointSet & other)
266  {
267  if (this == &other)
268  return *this;
269 
270  roots_ = other.roots_;
271  values_ = other.values_;
272 
273  return *this;
274  }
275 
276  DisjointSet &
278  {
279  if (this == &other)
280  return *this;
281 
282  roots_ = std::move(other.roots_);
283  values_ = std::move(other.values_);
284 
285  return *this;
286  }
287 
288  void
289  insert(const std::vector<T> & elements)
290  {
291  for (auto & element : elements)
292  insert(element);
293  }
294 
295  const Set *
296  insert(const T & element)
297  {
298  if (contains(element))
299  return values_.find(element)->second.get();
300 
301  values_[element] = Set::create(element);
302  auto s = values_.find(element)->second.get();
303  roots_.insert(s);
304  return s;
305  }
306 
307  SetIterator
308  begin() const
309  {
310  return roots_.begin();
311  }
312 
313  SetIterator
314  end() const
315  {
316  return roots_.end();
317  }
318 
319  bool
320  empty() const noexcept
321  {
322  return values_.empty();
323  }
324 
325  size_t
326  nvalues() const noexcept
327  {
328  return values_.size();
329  }
330 
331  size_t
332  nsets() const noexcept
333  {
334  return roots_.size();
335  }
336 
337  void
339  {
340  roots_.clear();
341  values_.clear();
342  }
343 
344  /*
345  @brief Find the representative set for the set containing \p element.
346  */
347  const Set *
348  find(const T & element) const noexcept
349  {
350  JLM_ASSERT(contains(element));
351 
352  return values_.find(element)->second->root();
353  }
354 
355  /*
356  @brief Find the representative set for the set containing \p element
357  if \p element is present. Otherwise, insert \p element and
358  return the representative set.
359  */
360  const Set *
361  find_or_insert(const T & element) noexcept
362  {
363  if (!contains(element))
364  return insert(element);
365 
366  return find(element);
367  }
368 
369  /*
370  @brief Union the two sets containing elements \p e1 and \p e2.
371  */
372  const Set *
373  merge(const T & e1, const T & e2)
374  {
375  auto root1 = find(e1);
376  auto root2 = find(e2);
377 
378  /* Both elements are already in the same set. */
379  if (root1 == root2)
380  return root1;
381 
382  /* union by size */
383  auto size1 = root1->size_;
384  auto size2 = root2->size_;
385  if (size1 < size2)
386  std::swap(root1, root2);
387 
388  /* Insert elements of smaller set into bigger set */
389  std::swap(root1->next_, root2->next_);
390 
391  roots_.erase(root2);
392  root2->parent_ = root1;
393  root1->size_ = size1 + size2;
394 
395  return root1;
396  }
397 
398 private:
399  bool
400  contains(const T & element) const
401  {
402  return values_.find(element) != values_.end();
403  }
404 
405  std::unordered_set<const Set *> roots_;
406  std::unordered_map<T, std::unique_ptr<Set>> values_;
407 };
408 
409 }
410 
411 #endif
bool operator!=(const MemberIterator &other) const
Definition: disjointset.hpp:80
bool operator==(const MemberIterator &other) const
Definition: disjointset.hpp:74
std::forward_iterator_tag iterator_category
Definition: disjointset.hpp:29
bool operator==(const SetIterator &other) const
SetIterator(const typename std::unordered_set< const Set * >::const_iterator &it)
std::forward_iterator_tag iterator_category
bool operator!=(const SetIterator &other) const
std::unordered_set< const Set * >::const_iterator it_
bool is_root() const noexcept
Set(Set &&other)=delete
Set(const Set &)=delete
Set & operator=(Set &&other)=delete
bool operator!=(const Set &other) const noexcept
size_t nmembers() const noexcept
MemberIterator begin() const
Set & operator=(const Set &)=delete
MemberIterator end() const
const Set * root() const noexcept
const T & value() const noexcept
bool operator==(const Set &other) const noexcept
static std::unique_ptr< Set > create(const T &value)
bool empty() const noexcept
DisjointSet & operator=(const DisjointSet &other)
const Set * merge(const T &e1, const T &e2)
void insert(const std::vector< T > &elements)
size_t nvalues() const noexcept
size_t nsets() const noexcept
const Set * find(const T &element) const noexcept
DisjointSet(const std::vector< T > &elements)
std::unordered_map< T, std::unique_ptr< Set > > values_
constexpr DisjointSet()=default
DisjointSet(DisjointSet &&other)
const Set * find_or_insert(const T &element) noexcept
DisjointSet(const DisjointSet &other)
DisjointSet & operator=(DisjointSet &&other)
const Set * insert(const T &element)
SetIterator end() const
SetIterator begin() const
bool contains(const T &element) const
std::unordered_set< const Set * > roots_
#define JLM_ASSERT(x)
Definition: common.hpp:16