programming-examples/java/Data_Structures/DisjointSet.java
2019-11-15 12:59:38 +01:00

139 lines
4.5 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.jwetherell.algorithms.data_structures;
/**
* In computer science, a disjoint-set data structure, also called a unionfind data structure or mergefind set, is a data structure that keeps track of a set of
* elements partitioned into a number of disjoint (non-overlapping) subsets.
*
* It supports two useful operations:
* Find: Determine which subset a particular element is in. Find typically returns an item from this set that serves as its "representative"; by comparing the
* result of two Find operations, one can determine whether two elements are in the same subset.
* Union: Join two subsets into a single subset.
*
* http://en.wikipedia.org/wiki/Disjoint-set_data_structure
*
* @author Justin Wetherell <phishman3579@gmail.com>
*/
@SuppressWarnings("unchecked")
public class DisjointSet<T extends Object> {
private DisjointSet() { }
/**
* Creates a set of one element.
*
* @param v Value to use when creating the set
* @return Item representing the value
*/
public static final <T extends Object> Item<T> makeSet(T v) {
final Item<T> item = new Item<T>(null,v);
item.parent = item;
return item;
}
/**
* Determine which subset a particular element is in. Find returns an item from this set that serves as its "representative"; by comparing the result
* of two Find operations, one can determine whether two elements are in the same subset. This method uses path compression which is a way of flattening
* the structure of the tree whenever Find is used on it.
*
* @param x Find the "representative" of this Item
* @return "Representative" of this Item
*/
public static final <T extends Object> Item<T> find(Item<T> x) {
if (x == null)
return null;
if ((x.parent!=null) && !(x.parent.equals(x)))
return x.parent = find(x.parent);
return x.parent;
}
/**
* Join two subsets into a single subset. This method uses 'union by rank' which always attaches the smaller tree to the root of the larger tree.
*
* @param x Subset 1 to join
* @param y Subset 2 to join
* @return Resulting Set of joining Subset 1 and Subset 2
*/
public static final <T extends Object> Item<T> union(Item<T> x, Item<T> y) {
final Item<T> xRoot = find(x);
final Item<T> yRoot = find(y);
if (xRoot==null && yRoot==null)
return null;
if (xRoot==null && yRoot!=null)
return yRoot;
if (yRoot==null && xRoot!=null)
return xRoot;
// x and y are in the same set
if (xRoot.equals(yRoot))
return xRoot;
if (xRoot.rank < yRoot.rank) {
xRoot.parent = yRoot;
return yRoot;
} else if (xRoot.rank > yRoot.rank) {
yRoot.parent = xRoot;
return xRoot;
}
// else
yRoot.parent = xRoot;
xRoot.rank = xRoot.rank + 1;
return xRoot;
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return "Nothing here to see, yet.";
}
public static final class Item<T> {
private Item<T> parent;
private T value;
/** Rank is not the actual depth of the tree rather it is an upper bound. As such, on a find operation, the rank is allowed to get out of sync with the depth. **/
private long rank;
public Item(Item<T> parent, T value) {
this.parent = parent;
this.value = value;
this.rank = 0;
}
public T getValue() {
return value;
}
public long getRank() {
return rank;
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object o) {
if (!(o instanceof Item))
return false;
final Item<T> i = (Item<T>) o;
if ((i.parent!=null && parent!=null) && !(i.parent.value.equals(parent.value)))
return false;
if ((i.value!=null && value!=null) && !(value.equals(value)))
return false;
return true;
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return "parent="+(parent!=null?parent.value:null)+" "+
(rank>0?"rank="+rank +" " : "") +
"value="+(value!=null?value:null);
}
}
}