-
Notifications
You must be signed in to change notification settings - Fork 12
49 add union find data structure #62
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
63a45e6
137ad6e
a6820bf
42aa243
cb5d31d
27a7728
7d0afc7
33e3326
b301753
4c52fc0
5baa93d
24e376b
00d285d
6e04f27
a7959db
831ec68
cb0ca0e
b2902e2
fdcd004
eaadc94
b12540f
d9b5dc3
41ae904
188b203
88f484d
b432119
94f1d26
97b7bd3
2bafeee
3e1f07a
9504ebb
15e7096
f4c5213
0ee0893
71b0f16
d07c875
0a8f5ec
469685f
3e42ae7
f765a9c
6221788
7a17558
a3ce540
493a220
7370d47
61437f3
352907c
92088a1
d2234c8
fca3102
4dd0356
f7ebd96
89e3a79
f452274
a0dacef
56133be
2f01c62
efc2bcb
9f939ba
2f4969d
0782511
37770d5
9334994
ea44c05
ee6fb37
9c86ef7
9b27c60
5917c15
d67006e
69f5fa6
1898af1
52571a8
5d6cca6
2dab552
43b4aaa
61278c7
919356e
6bee232
9f69580
af996d2
344cd5f
761acec
e9e53d7
eb1c8b8
599310b
4de8043
220ec5c
1834c73
3a54e64
a180bcc
b9efedc
056f4e1
a93864c
47a8e3f
ebb1322
9b9be09
87a1fb1
2a7248f
5dfd680
aedbd22
54b57a8
e3d08a9
a1cd3b9
d816adb
53c3737
d72cd52
3b1644a
4341fb0
166ca86
4f9b6ab
b9c6473
c9d7379
fe3ad12
f0320e4
8a8f25c
452c919
face617
8f9a3cb
f2ef0f0
9293ac0
3fdb15d
82c9e3c
23793c0
be98782
4aebe06
c9da608
352a2b6
f3f3764
842e5c0
cba998f
84f91e9
06c1648
27e702e
a58eaee
1f6f4cc
4206ed7
0d464c4
aa3e1f4
57a0fe7
0c9ba04
fee7bf6
f97d033
0c0d850
90d3479
8300628
afec034
307b5b0
f9aa19b
3bd8648
c403afd
c835aca
2f50963
cc17a11
4800d6c
d640822
9a45b3e
7949ab9
8b62779
4af6da2
716a90a
06a31f3
81b99c8
33fb798
225cc24
fc834a6
8795910
a3f499a
85fdfbb
4ee5fcf
36d5aaa
aa10c26
21deda2
3ec0c88
cd7c1cd
6ce45fb
d888c6b
2fb0dfa
9933544
d3455a8
07e9a8b
687fa8e
988ee79
a01f37a
12c03eb
1803638
b75ffbd
8eb0aa4
5e4f0f9
22e98d7
d2ac186
e5c717e
44db575
537f4ec
0b0643f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,179 @@ | ||
| // This file is part of SoSy-Lab Common, | ||
| // a library of useful utilities: | ||
| // https://github.com/sosy-lab/java-common-lib | ||
| // | ||
| // SPDX-FileCopyrightText: 2026 Dirk Beyer <https://www.sosy-lab.org> | ||
| // | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| package org.sosy_lab.common.collect.union_find; | ||
|
|
||
| import com.google.common.base.Preconditions; | ||
| import java.util.Collection; | ||
| import java.util.Map; | ||
| import java.util.Map.Entry; | ||
| import java.util.Set; | ||
|
|
||
| /** | ||
| * An abstract, generic implementation of {@link UnionFind} using a {@link Map} of {@link Set}s. In | ||
| * order to represent subsets by canonical elements, each one is mapped to its representative | ||
| * canonical element. This is always the first element added to the subset, unless it has changed | ||
| * due to union operations. The union is implemented as union by size. | ||
| * | ||
| * @param <T> type of elements added to the Union-Find. | ||
| */ | ||
| public abstract class AbstractGenericUnionFind<T, S extends Set<T>, M extends Map<T, S>> | ||
| implements UnionFind<T> { | ||
|
|
||
| protected final M mapOfSets; | ||
|
|
||
| /** | ||
| * Takes an empty map of the desired kind and allocates it to the variable mapOfSets. This enables | ||
| * child classes to simply pass an object of the desired kind without having to modify the | ||
| * constructor and methods. | ||
| */ | ||
| @SuppressWarnings("unchecked") | ||
| public AbstractGenericUnionFind() { | ||
| mapOfSets = (M) getEmptyMap(); | ||
| } | ||
|
|
||
| /** | ||
| * Returns the canonical element of the set containing the provided element. | ||
| * | ||
| * @param pE element for which set is to be found | ||
| * @return canonical element of the found set | ||
| * @throws IllegalArgumentException if element is not contained in any subset | ||
| */ | ||
| @Override | ||
| public T find(T pE) { | ||
|
|
||
| Preconditions.checkNotNull(pE); | ||
|
|
||
| for (Entry<T, S> mapping : mapOfSets.entrySet()) { | ||
| if (mapping.getValue().contains(pE)) { | ||
| return mapping.getKey(); | ||
| } | ||
| } | ||
|
|
||
| throw new IllegalArgumentException("Element not contained"); | ||
| } | ||
|
|
||
| /** | ||
| * Merges the sets represented by the two input values according to standard Union-Find behaviour. | ||
| * | ||
| * <p>USES: Add new element as new set: pass it as both pE1 and pE2. Add new element to existing | ||
| * set: one input value is the new element, the other the canonical element of the set to be added | ||
| * to. Merge two existing sets: pE1, pE2 canonical elements of sets to be merged. | ||
| * | ||
| * @param pE1 first element | ||
| * @param pE2 second element | ||
| */ | ||
| @Override | ||
| public void union(T pE1, T pE2) { | ||
|
|
||
| Preconditions.checkNotNull(pE1); | ||
| Preconditions.checkNotNull(pE2); | ||
|
|
||
| if (pE1.equals(pE2)) { | ||
| addElementAsNewSet(pE1); | ||
| } else { | ||
| Set<T> canonicalElements = mapOfSets.keySet(); | ||
|
|
||
| if (canonicalElements.contains(pE1)) { | ||
| if (canonicalElements.contains(pE2)) { | ||
| mergeExistingSets(pE1, pE2); | ||
| } else { | ||
| addElementToExistingSet(pE2, pE1); | ||
| } | ||
| } else if (canonicalElements.contains(pE2)) { | ||
| addElementToExistingSet(pE1, pE2); | ||
| } else { | ||
|
|
||
| if (contains(pE1)) { | ||
| if (contains(pE2)) { | ||
| mergeExistingSets(find(pE1), find(pE2)); | ||
| } else { | ||
| addElementToExistingSet(pE2, find(pE1)); | ||
| } | ||
| } else { | ||
| addElementAsNewSet(pE1); | ||
| addElementToExistingSet(pE2, pE1); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| @SuppressWarnings("unchecked") | ||
| private void addElementAsNewSet(T pE) { | ||
|
|
||
| if (!contains(pE)) { | ||
| S newSet = (S) getEmptySet(); | ||
| newSet.add(pE); | ||
| mapOfSets.put(pE, newSet); | ||
| } | ||
| } | ||
|
|
||
| private void addElementToExistingSet(T pE, T pCanon) { | ||
|
|
||
| if (!contains(pE)) { | ||
| mapOfSets.get(pCanon).add(pE); | ||
| } else { | ||
| mergeExistingSets(find(pE), pCanon); | ||
| } | ||
| } | ||
|
|
||
| // pE1 will be new canonical element only if its set is actually bigger, otherwise pE2 new canon | ||
| private void mergeExistingSets(T pE1, T pE2) { | ||
|
|
||
| S set1 = mapOfSets.get(pE1); | ||
| S set2 = mapOfSets.get(pE2); | ||
|
|
||
| assert set1 != null; | ||
| assert set2 != null; | ||
|
|
||
| int size1 = set1.size(); | ||
| int size2 = set2.size(); | ||
|
|
||
| if (size1 > size2) { | ||
| set1.addAll(set2); | ||
| assert mapOfSets.remove(pE2, set2); | ||
| } else { | ||
| set2.addAll(set1); | ||
| assert mapOfSets.remove(pE1, set1); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Provides a {@link Collection} containing all current subsets. | ||
| * | ||
| * @return {@link Collection} containing all current subsets | ||
| */ | ||
| @Override | ||
| public Collection<S> getAllSubsets() { | ||
| return mapOfSets.values(); | ||
| } | ||
|
|
||
| /** | ||
| * Checks whether the provided element is contained in any current subset and returns true or | ||
| * false accordingly. | ||
| * | ||
| * @param pE element to be searched for | ||
| * @return true if contained, false if not | ||
| */ | ||
| @Override | ||
| public boolean contains(T pE) { | ||
|
|
||
| Preconditions.checkNotNull(pE); | ||
|
|
||
| for (S current : mapOfSets.values()) { | ||
| if (current.contains(pE)) { | ||
| return true; | ||
| } | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| protected abstract Set<T> getEmptySet(); | ||
|
|
||
| protected abstract Map<T, Set<T>> getEmptyMap(); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| // This file is part of SoSy-Lab Common, | ||
| // a library of useful utilities: | ||
| // https://github.com/sosy-lab/java-common-lib | ||
| // | ||
| // SPDX-FileCopyrightText: 2026 Dirk Beyer <https://www.sosy-lab.org> | ||
| // | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| package org.sosy_lab.common.collect.union_find; | ||
|
|
||
| import com.google.errorprone.annotations.CanIgnoreReturnValue; | ||
| import com.google.errorprone.annotations.Immutable; | ||
| import java.util.Collection; | ||
| import java.util.Set; | ||
| import org.sosy_lab.common.collect.union_find.ParentPointerTreeUnionFind.UnionType; | ||
|
|
||
| /** | ||
| * Abstract builder class which first collects the data in a mutable Union-Find and converts it to | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is "the data"? What form is the data supposed to have? What is allowed, what is disallowed? Are all "data" always collected into a single set inside the Union-Find? |
||
| * an immutable Union-Find when {@code build()} is called. From then onward, previously modifying | ||
| * methods will not cause further modifications to the Union-Find instance inside. See documentation | ||
| * in {@link ParentPointerTreeUnionFind} for explanations on {@code union()}, {@code add()} and | ||
| * {@code addAll()} as the methods in this builder simply pass to their aforementioned namesakes. | ||
| * | ||
| * @param <T> type of elements added to the Union-Find | ||
| */ | ||
| @Immutable(containerOf = "T") | ||
| public abstract class AbstractImmutableParentPointerTreeBuilder<T> { | ||
|
|
||
| // union-find not immutable but only used internally and never mutated passed outward | ||
| // build() returns an immutable union-find that contains a copy of this union-find's map | ||
| @SuppressWarnings("Immutable") | ||
| final ParentPointerTreeUnionFind<T> unionFind; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing privacy modifier. |
||
|
|
||
| // prevents further modifications after build() | ||
| // is never modified once having been switched to false | ||
| @SuppressWarnings("Immutable") | ||
| boolean modificationsAllowed; | ||
|
|
||
| protected AbstractImmutableParentPointerTreeBuilder(UnionType pUnionType) { | ||
| unionFind = new ParentPointerTreeUnionFind<>(pUnionType); | ||
| modificationsAllowed = true; | ||
| } | ||
|
|
||
| @CanIgnoreReturnValue | ||
| public AbstractImmutableParentPointerTreeBuilder<T> union(T pE1, T pE2) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Most of the documentation in this class is missing, |
||
|
|
||
| if (modificationsAllowed) { | ||
|
|
||
| unionFind.union(pE1, pE2); | ||
| } | ||
|
|
||
| return this; | ||
| } | ||
|
|
||
| @CanIgnoreReturnValue | ||
| public AbstractImmutableParentPointerTreeBuilder<T> add(Set<T> pSet) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing documentation. What happens if this set contains elements that are already in the union-find? |
||
|
|
||
| if (modificationsAllowed) { | ||
|
|
||
| unionFind.add(pSet); | ||
| } | ||
|
|
||
| return this; | ||
| } | ||
|
|
||
| @CanIgnoreReturnValue | ||
| public AbstractImmutableParentPointerTreeBuilder<T> addAll(Collection<Set<T>> pSets) { | ||
|
|
||
| if (modificationsAllowed) { | ||
|
|
||
| unionFind.addAll(pSets); | ||
| } | ||
|
|
||
| return this; | ||
| } | ||
|
|
||
| // get map from mutable Union-Find instance and convert to immutable map, then pass to constructor | ||
| // set modificationsAllowed to false!! | ||
| public abstract AbstractImmutableUnionFind<T> build(); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| // This file is part of SoSy-Lab Common, | ||
| // a library of useful utilities: | ||
| // https://github.com/sosy-lab/java-common-lib | ||
| // | ||
| // SPDX-FileCopyrightText: 2026 Dirk Beyer <https://www.sosy-lab.org> | ||
| // | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| package org.sosy_lab.common.collect.union_find; | ||
|
|
||
| import com.google.errorprone.annotations.Immutable; | ||
|
|
||
| /** | ||
| * An abstract class for sorted immutable Union-Find implementations. | ||
| * | ||
| * @param <T> type of elements added to the Union-Find. Must be comparable. | ||
| */ | ||
| @Immutable(containerOf = "T") | ||
| public abstract class AbstractImmutableSortedUnionFind<T extends Comparable<T>> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not use a interface? |
||
| extends AbstractImmutableUnionFind<T> implements SortedUnionFind<T> {} | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| // This file is part of SoSy-Lab Common, | ||
| // a library of useful utilities: | ||
| // https://github.com/sosy-lab/java-common-lib | ||
| // | ||
| // SPDX-FileCopyrightText: 2026 Dirk Beyer <https://www.sosy-lab.org> | ||
| // | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| package org.sosy_lab.common.collect.union_find; | ||
|
|
||
| import com.google.errorprone.annotations.DoNotCall; | ||
| import com.google.errorprone.annotations.Immutable; | ||
|
|
||
| /** | ||
| * An abstract class for immutable Union-Find implementations. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This description is not helpful. It doesn't tell users or developers anything about what to expect from this class or its inheritors. |
||
| * | ||
| * @param <T> type of elements added to the Union-Find | ||
| */ | ||
| @Immutable(containerOf = "T") | ||
| public abstract class AbstractImmutableUnionFind<T> implements UnionFind<T> { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is there no interface |
||
|
|
||
| @Deprecated | ||
| @Override | ||
| @DoNotCall | ||
| public final void union(T pE1, T pE2) { | ||
| throw new UnsupportedOperationException(); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| // This file is part of SoSy-Lab Common, | ||
| // a library of useful utilities: | ||
| // https://github.com/sosy-lab/java-common-lib | ||
| // | ||
| // SPDX-FileCopyrightText: 2026 Dirk Beyer <https://www.sosy-lab.org> | ||
| // | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| package org.sosy_lab.common.collect.union_find; | ||
|
|
||
| /** | ||
| * An abstract class of nodes from which a simple parent pointer tree can be built. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How to discern leafs and roots in this implementation? The documentation of how to use this to traverse the tree is completely missing. |
||
| * | ||
| * @param <T> type of elements each node holds as value | ||
| */ | ||
| public abstract class AbstractTreeNode<T> { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
|
||
| private AbstractTreeNode<T> parent; | ||
| private final T value; | ||
|
|
||
| /** | ||
| * Constructor for a root node. The parent variable points to itself, thus indicating this is a | ||
| * root node. If appended to another tree, parent can be reallocated to the new parent node, while | ||
| * the current node simply functions as a non-root node from then on. | ||
| * | ||
| * @param pValue element to be stored in the node | ||
| */ | ||
| protected AbstractTreeNode(T pValue) { | ||
| parent = this; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This may lead to infinite recursion when traversing along |
||
| value = pValue; | ||
| } | ||
|
|
||
| /** | ||
| * Constructor for a non-root node. | ||
| * | ||
| * @param pParent pParent node (can be root or non-root) | ||
| * @param pValue element to be stored in the node | ||
| */ | ||
| protected AbstractTreeNode(AbstractTreeNode<T> pParent, T pValue) { | ||
| parent = pParent; | ||
| value = pValue; | ||
| } | ||
|
|
||
| public AbstractTreeNode<T> getParent() { | ||
| return parent; | ||
| } | ||
|
|
||
| public void setParent(AbstractTreeNode<T> pParent) { | ||
| parent = pParent; | ||
| } | ||
|
|
||
| public T getValue() { | ||
| return value; | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assertions are not evaluated if the correct flags are not set and should never contain actual code.