using System;
using System.Collections.Generic;
using System.Text;
namespace EmilStefanov
{
public class DisjointSets
{
///
/// Create an empty DisjointSets data structure
///
public DisjointSets()
: this(0)
{
}
///
/// Create a DisjointSets data structure with a specified number of elements (with element id's from 0 to count-1)
///
///
public DisjointSets(int count)
{
m_elementCount = 0;
m_setCount = 0;
m_nodes = new List();
AddElements(count);
}
///
/// Find the set identifier that an element currently belongs to.
/// Note: some internal data is modified for optimization even though this method is consant.
///
///
///
public int FindSet(int elementId)
{
if(elementId >= m_elementCount)
throw new ArgumentOutOfRangeException("elementId");
Node curNode;
// Find the root element that represents the set which `elementId` belongs to
curNode = m_nodes[elementId];
while(curNode.Parent != null)
curNode = curNode.Parent;
Node root = curNode;
// Walk to the root, updating the parents of `elementId`. Make those elements the direct
// children of `root`. This optimizes the tree for future FindSet invokations.
curNode = m_nodes[elementId];
while(curNode != root)
{
Node next = curNode.Parent;
curNode.Parent = root;
curNode = next;
}
return root.Index;
}
///
/// Combine two sets into one. All elements in those two sets will share the same set id that can be gotten using FindSet.
///
///
///
public void Union(int setId1, int setId2)
{
if(setId1 >= m_elementCount)
throw new ArgumentOutOfRangeException("setId1");
if(setId2 >= m_elementCount)
throw new ArgumentOutOfRangeException("setId2");
if(setId1 == setId2)
return; // already unioned
Node set1 = m_nodes[setId1];
Node set2 = m_nodes[setId2];
// Determine which node representing a set has a higher rank. The node with the higher rank is
// likely to have a bigger subtree so in order to better balance the tree representing the
// union, the node with the higher rank is made the parent of the one with the lower rank and
// not the other way around.
if(set1.Rank > set2.Rank)
set2.Parent = set1;
else if(set1.Rank < set2.Rank)
set1.Parent = set2;
else // set1.Rank == set2.Rank
{
set2.Parent = set1;
++set1.Rank; // update rank
}
// Since two sets have fused into one, there is now one less set so update the set count.
--m_setCount;
}
///
/// Add a specified number of elements to the DisjointSets data structure. The element id's of the new elements are numbered
/// consequitively starting with the first never-before-used elementId.
///
///
public void AddElements(int addCount)
{
if(addCount < 0)
throw new ArgumentOutOfRangeException("addCount");
// insert and initialize the specified number of element nodes to the end of the `m_nodes` array
for(int i = m_elementCount; i < m_elementCount + addCount; ++i)
{
Node newNode = new Node();
newNode.Parent = null;
newNode.Index = i;
newNode.Rank = 0;
m_nodes.Add(newNode);
}
// update element and set counts
m_elementCount += addCount;
m_setCount += addCount;
}
///
/// Returns the number of elements currently in the DisjointSets data structure.
///
public int ElementCount
{
get { return m_elementCount; }
}
///
/// Returns the number of sets currently in the DisjointSets data structure.
///
public int SetCount
{
get { return m_setCount; }
}
///
/// Internal Node data structure used for representing an element.
///
private class Node
{
///
/// This roughly represent the max height of the node in its subtree.
///
public int Rank;
///
/// The index of the element the node represents.
///
public int Index;
///
/// The parent node of the node.
///
public Node Parent;
}
///
/// The number of elements currently in the DisjointSets data structure.
///
private int m_elementCount;
///
/// The number of sets currently in the DisjointSets data structure.
///
private int m_setCount;
///
/// The list of nodes representing the elements.
///
private List m_nodes;
}
}