blob: dbc6016d091f1b4eb6e18eef2c22c49375d52798 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006 IBM Corporation and others.
* 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:
* IBM Corporation - initial API and implementation
******************************************************************************/
package org.eclipse.ui.internal.ide.misc;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
/**
* A disjoint set is a generic data structure that represents a collection of
* sets that are assumed to be disjoint (no object exists in more than
* one set).
* <p>
* This disjoint set implementation represents the disjoint set as a forest,
* where the nodes of each tree all belong to the same set. This implementation
* uses path compression in the findSet implementation to flatten each tree
* to a constant depth. A rank is maintained for each tree that is used when
* performing union operations to ensure the tree remains balanced.
* <p>
* Ref: Cormen, Leiserson, and Rivest <it>Introduction to Algorithms</it>,
* McGraw-Hill, 1990. The disjoint set forest implementation in section 22.3.
* </p>
* @since 3.2
*/
public class DisjointSet {
/**
* A node in the disjoint set forest. Each tree in the forest is
* a disjoint set, where the root of the tree is the set representative.
*/
private static class Node {
/** The node rank used for union by rank optimization */
int rank;
/** The parent of this node in the tree. */
Object parent;
Node(Object parent, int rank) {
this.parent = parent;
this.rank = rank;
}
}
/**
* Map of Object -> Node, where each key is an object in the
* disjoint set, and the Node represents its position and rank
* within the set.
*/
private final HashMap objectsToNodes = new HashMap();
/**
* Returns the set token for the given object, or null if the
* object does not belong to any set. All object
* in the same set have an identical set token.
* @param o The object to return the set token for
* @return The set token, or <code>null</code>
*/
public Object findSet(Object o) {
DisjointSet.Node node = (DisjointSet.Node) objectsToNodes.get(o);
if (node == null)
return null;
if (o != node.parent)
node.parent = findSet(node.parent);
return node.parent;
}
/**
* Adds a new set to the group of disjoint sets for the given object.
* It is assumed that the object does not yet belong to any set.
* @param o The object to add to the set
*/
public void makeSet(Object o) {
objectsToNodes.put(o, new Node(o, 0));
}
/**
* Removes all elements belonging to the set of the given object.
* @param o The object to remove
*/
public void removeSet(Object o) {
Object set = findSet(o);
if (set == null)
return;
for (Iterator it = objectsToNodes.keySet().iterator(); it.hasNext();) {
Object next = it.next();
//remove the set representative last, otherwise findSet will fail
if (next != set && findSet(next) == set)
it.remove();
}
objectsToNodes.remove(set);
}
/**
* Copies all objects in the disjoint set to the provided list
* @param list The list to copy objects into
*/
public void toList(List list) {
list.addAll(objectsToNodes.keySet());
}
/**
* Unions the set represented by token x with the set represented by
* token y. Has no effect if either x or y is not in the disjoint set, or
* if they already belong to the same set.
* @param x The first set to union
* @param y The second set to union
*/
public void union(Object x, Object y) {
Object setX = findSet(x);
Object setY = findSet(y);
if (setX == null || setY == null || setX == setY)
return;
DisjointSet.Node nodeX = (DisjointSet.Node) objectsToNodes.get(setX);
DisjointSet.Node nodeY = (DisjointSet.Node) objectsToNodes.get(setY);
//join the two sets by pointing the root of one at the root of the other
if (nodeX.rank > nodeY.rank) {
nodeY.parent = x;
} else {
nodeX.parent = y;
if (nodeX.rank == nodeY.rank)
nodeY.rank++;
}
}
}