Jlm
Loading...
Searching...
No Matches
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
16namespace jlm::util
17{
18
19template<class T>
20class DisjointSet final
21{
22public:
23 class Set;
24
25private:
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:
44 operator*() const
45 {
46 JLM_ASSERT(node_ != nullptr);
47 return node_->value();
48 }
49
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
89public:
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 *
211 {
212 return &operator*();
213 }
214
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
246public:
247 constexpr DisjointSet() = default;
248
249 explicit DisjointSet(const std::vector<T> & elements)
250 {
251 insert(elements);
252 }
253
255 {
256 operator=(other);
257 }
258
260 {
261 operator=(other);
262 }
263
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
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
398private:
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
bool operator==(const MemberIterator &other) const
std::forward_iterator_tag iterator_category
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< constSet * >::const_iterator it_
bool is_root() const noexcept
Set(Set &&other)=delete
Set(const Set &)=delete
Set & operator=(Set &&other)=delete
static std::unique_ptr< Set > create(const T &value)
bool operator!=(const Set &other) const noexcept
size_t nmembers() const noexcept
MemberIterator begin() const
const T & value() const noexcept
MemberIterator end() const
const Set * root() const noexcept
Set & operator=(const Set &)=delete
bool operator==(const Set &other) const noexcept
bool empty() const noexcept
DisjointSet & operator=(DisjointSet &&other)
const Set * merge(const T &e1, const T &e2)
const Set * find_or_insert(const T &element) noexcept
void insert(const std::vector< T > &elements)
size_t nvalues() const noexcept
const Set * insert(const T &element)
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)
DisjointSet(const DisjointSet &other)
SetIterator end() const
SetIterator begin() const
DisjointSet & operator=(const DisjointSet &other)
bool contains(const T &element) const
std::unordered_set< const Set * > roots_
#define JLM_ASSERT(x)
Definition common.hpp:16