Working my way through Algorithms, Fourth edition by Robert Sedgewick & Kevin Wayne. I implemented a generic version of the Quick-Union algorithm and added Path Compression (not described in the book) to it.
Mainly I'm looking for feedback on these two aspects and whether I might have overlooked a side effect.
public class WeightedQuickUnionWithPathCompression<T> {
private final Map<T, T> components = new HashMap<>();
private final Map<T, Integer> treeSizes = new HashMap<>();
public WeightedQuickUnionWithPathCompression(T[] components) {
for (T component : components) {
this.components.put(component, component);
this.treeSizes.put(component, 1);
}
}
public void connect(T leftComponent, T rightComponent) {
Objects.requireNonNull(leftComponent);
Objects.requireNonNull(rightComponent);
if (areConnected(leftComponent, rightComponent)) {
return;
}
T leftComponentTree = find(leftComponent);
T rightComponentTree = find(rightComponent);
int leftTreeSize = getTreeSize(leftComponentTree);
int rightTreeSize = getTreeSize(rightComponentTree);
if (leftTreeSize < rightTreeSize) {
components.put(leftComponentTree, rightComponentTree);
treeSizes.put(rightComponentTree, leftTreeSize + rightTreeSize);
} else {
components.put(rightComponentTree, leftComponentTree);
treeSizes.put(leftComponentTree, leftTreeSize + rightTreeSize);
}
}
private T find(T component) {
while (!component.equals(components.get(component))) {
components.put(component, components.get(components.get(component))); // Path compression
component = components.get(component);
}
return component;
}
private int getTreeSize(T component) {
return treeSizes.get(component);
}
public boolean areConnected(T leftComponent, T rightComponent) {
Objects.requireNonNull(leftComponent);
Objects.requireNonNull(rightComponent);
return find(leftComponent).equals(find(rightComponent));
}
}