139 lines
4.5 KiB
Java
139 lines
4.5 KiB
Java
|
package com.jwetherell.algorithms.data_structures;
|
|||
|
|
|||
|
/**
|
|||
|
* In computer science, a disjoint-set data structure, also called a union–find data structure or merge–find 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);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|