
aima.core.util.DisjointSets Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aima-core Show documentation
Show all versions of aima-core Show documentation
AIMA-Java Core Algorithms from the book Artificial Intelligence a Modern Approach 3rd Ed.
package aima.core.util;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
/**
* A basic implementation of a disjoint-set data structure for maintaining a
* collection of disjoint dynamic sets. This is based on the algorithm
* description in Chapter 21 of 'Introduction to Algorithm 2nd Edition' (by
* Cormen, Leriserson, Rivest, and Stein) for Disjoint-sets taking into account
* some of the heuristic ideas described. However, this implementation relies on
* the constant time performance of the HashMap's get and put operations, and
* HashSet's add, remove, contains and size operations as an alternative
* implementation approach. This provides a cleaner separation between the
* elements and the implementation (i.e. the idea of a representative for a
* particular disjoint set is not used) and an easier to use API (i.e. direct
* access to the disjoint set that an element belongs to and no need to
* understand how the internals work with respect to a representative) but will
* not perform as fast (proper analysis required but likely O(m + n lg n) as
* detailed in Theorem 21.1 on page 504 of 'Introduction to Algorithm 2nd
* Edition').
*
* Note: the internal implementation of this class can likely be improved to use
* all the techniques outlined in section 21.3 in order to get optimal
* performance.
*
* @author Ciaran O'Reilly
*
* @param
* the type of elements to be contained by the disjoint sets.
*/
public class DisjointSets {
private Map> elementToSet = new LinkedHashMap>();
private Set> disjointSets = new LinkedHashSet>();
/**
* Default Constructor.
*/
public DisjointSets() {
}
/**
* Constructor.
*
* @param initialElements
* a collection of elements, each of which will be assigned to
* their own disjoint set via makeSet().
*/
public DisjointSets(Collection initialElements) {
for (E element : initialElements) {
makeSet(element);
}
}
/**
* Constructor.
*
* @param initialElements
* a collection of elements, each of which will be assigned to
* their own disjoint set via makeSet().
*/
public DisjointSets(E... elements) {
for (E element : elements) {
makeSet(element);
}
}
/**
* Create a disjoint set for the element passed in. This method should be
* called for all elements before any of the other API methods are called
* (i.e. construct a disjoint set for each element first and then union them
* together as appropriate).
*
* @param element
* the element for which a new disjoint set should be
* constructed.
*/
public void makeSet(E element) {
if (!elementToSet.containsKey(element)) {
// Note: It is necessary to use an identity based hash set
// whose equal and hashCode method are based on the Sets
// identity and not its elements as we are adding
// this set to a set but changing its values as unions
// occur.
Set set = new IdentityHashSet();
set.add(element);
elementToSet.put(element, set);
disjointSets.add(set);
}
}
/**
* Union two disjoint sets together if the arguments currently belong to two
* different sets.
*
* @param element1
* @param element2
* @throws IllegalArgumentException
* if element1 or element 2 is not already associated with a
* disjoint set (i.e. makeSet() was not called for the argument
* beforehand).
*/
public void union(E element1, E element2) {
Set set1 = elementToSet.get(element1);
if (set1 == null) {
throw new IllegalArgumentException(
"element 1 is not associated with a disjoint set, call makeSet() first.");
}
Set set2 = elementToSet.get(element2);
if (set2 == null) {
throw new IllegalArgumentException(
"element 2 is not associated with a disjoint set, call makeSet() first.");
}
if (set1 != set2) {
// simple weighted union heuristic
if (set1.size() < set2.size()) {
set2.addAll(set1);
for (E element : set1) {
disjointSets.remove(elementToSet.put(element, set2));
}
}
else {
// i.e. set1 >= set2
set1.addAll(set2);
for (E element : set2) {
disjointSets.remove(elementToSet.put(element, set1));
}
}
}
}
/**
* Find the disjoint set that an element belongs to.
*
* @param element
* the element whose disjoint set is being sought.
* @return the disjoint set for the element or null if makeSet(element) was
* not previously called.
*/
public Set find(E element) {
// Note: Instantiate normal sets to ensure IdentityHashSet
// is not exposed outside of this class.
// This also ensures the internal logic cannot
// be corrupted externally due to changing sets.
return new LinkedHashSet(elementToSet.get(element));
}
/**
*
* @return a map for each element and the corresponding disjoint set that it
* belongs to.
*/
public Map> getElementToDisjointSet() {
// Note: Instantiate normal sets to ensure IdentityHashSet
// is not exposed outside of this class.
// This also ensures the internal logic cannot
// be corrupted externally due to changing sets.
Map> result = new LinkedHashMap>();
for (Map.Entry> entry : elementToSet.entrySet()) {
result.put(entry.getKey(), new LinkedHashSet(entry.getValue()));
}
return result;
}
/**
*
* @return the set of disjoint sets being maintained.
*/
public Set> getDisjointSets() {
// Note: Instantiate normal sets to ensure IdentityHashSet
// is not exposed outside of this class.
// This also ensures the internal logic cannot
// be corrupted externally due to changing sets.
Set> result = new LinkedHashSet>();
Iterator> it = disjointSets.iterator();
while (it.hasNext()) {
result.add(new LinkedHashSet(it.next()));
}
return result;
}
/**
*
* @return the number of disjoint sets.
*/
public int numberDisjointSets() {
return disjointSets.size();
}
/**
* Remove all the disjoint sets.
*/
public void clear() {
elementToSet.clear();
disjointSets.clear();
}
//
// PRIVATE METHODS
//
// Override hashCode and equals so that
// the Set itself and not its elements
// are used to determine its hashCode
// and equality.
private class IdentityHashSet extends HashSet {
private static final long serialVersionUID = 1L;
@Override
public int hashCode() {
return System.identityHashCode(this);
}
@Override
public boolean equals(Object o) {
return this == o;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy