/*******************************************************************************
* Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Tamas Szabo - initial API and implementation
*******************************************************************************/
package org.eclipse.incquery.runtime.base.itc.alg.incscc;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* Union-find data structure implementation. Note that the implementation relies on the correct implementation of the
* equals method of the type parameter's class.
*
* @author Tamas Szabo
*
* @param <V>
* the type parameter of the element's stored in the union-find data structure
*/
public class UnionFind<V> {
public Map<V, UnionFindNodeProperty<V>> nodeMap;
public Map<V, Set<V>> setMap;
/**
* Instantiate a new union-find data structure.
*/
public UnionFind() {
nodeMap = new HashMap<V, UnionFindNodeProperty<V>>();
setMap = new HashMap<V, Set<V>>();
}
/**
* Creates a new set from the array of elements.
*
* @param nodes
* the array of elements
* @return the root element
*/
public V makeSet(V[] nodes) {
if (nodes.length > 1) {
V root = makeSet(nodes[0]);
for (int i = 1; i < nodes.length; i++) {
root = union(nodes[i], root);
}
return root;
} else if (nodes.length == 1) {
return makeSet(nodes[0]);
} else {
return null;
}
}
/**
* Creates a new set from a collection of elements.
*
* @param nodes
* the collection of elements
* @return the root element
*/
public V makeSet(Collection<V> nodes) {
if (nodes.size() > 1) {
V root = makeSet(nodes.iterator().next());
for (V node : nodes) {
root = union(node, root);
}
return root;
} else if (nodes.size() == 1) {
return makeSet(nodes.iterator().next());
} else {
return null;
}
}
/**
* This method creates a single set containing the given node.
*
* @param node
* the root node of the set
* @return the root element
*/
public V makeSet(V node) {
if (!nodeMap.containsKey(node)) {
UnionFindNodeProperty<V> prop = new UnionFindNodeProperty<V>(0, node);
nodeMap.put(node, prop);
Set<V> set = new HashSet<V>();
set.add(node);
setMap.put(node, set);
}
return node;
}
/**
* Find method with path compression.
*
* @param node
* the node to find
* @return the root node of the set in which the given node can be found
*/
public V find(V node) {
UnionFindNodeProperty<V> prop = nodeMap.get(node);
if (prop != null) {
if (prop.parent.equals(node)) {
return node;
} else {
prop.parent = find(prop.parent);
return prop.parent;
}
}
return null;
}
/**
* Union by rank implementation of the two sets which contain x and y; x and/or y can be a single element from the
* universe.
*
* @param x
* set or single element of the universe
* @param y
* set or single element of the universe
* @return the new root of the two sets
*/
public V union(V x, V y) {
V xRoot = find(x);
V yRoot = find(y);
if ((xRoot == null) && (yRoot == null)) {
makeSet(x);
makeSet(y);
return union(x, y);
}
else if ((xRoot != null) && (yRoot == null)) {
makeSet(y);
return union(x, y);
}
else if ((xRoot == null) && (yRoot != null)) {
makeSet(x);
return union(x, y);
}
else if ((xRoot != null) && (yRoot != null) && !xRoot.equals(yRoot)) {
UnionFindNodeProperty<V> xRootProp = nodeMap.get(xRoot);
UnionFindNodeProperty<V> yRootProp = nodeMap.get(yRoot);
if (xRootProp.rank < yRootProp.rank) {
xRootProp.parent = yRoot;
setMap.get(yRoot).addAll(setMap.get(xRoot));
setMap.remove(xRoot);
return yRoot;
} else if (xRootProp.rank > yRootProp.rank) {
yRootProp.parent = xRoot;
setMap.get(xRoot).addAll(setMap.get(yRoot));
setMap.remove(yRoot);
return xRoot;
} else {
yRootProp.parent = xRoot;
xRootProp.rank += 1;
setMap.get(xRoot).addAll(setMap.get(yRoot));
setMap.remove(yRoot);
return xRoot;
}
} else
return xRoot;
}
/**
* Delete the set whose root is the given node.
*
* @param root
* the root node
*/
public void deleteSet(V root) {
// if (setMap.containsKey(root))
for (V n : setMap.get(root)) {
nodeMap.remove(n);
}
setMap.remove(root);
}
}