Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Main/src/CodeJam.Main.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@
<DesignTime>True</DesignTime>
<DependentUpon>DebugCode.tt</DependentUpon>
</Compile>
<Compile Include="Collections\DisjointSets.cs" />
<Compile Include="Collections\DisjointSetsBase.cs" />
<Compile Include="Collections\DisjointSetsT.cs" />
<Compile Include="Collections\EnumerableExtensions.Index.cs" />
<Compile Include="Collections\IndexedItem.cs" />
<Compile Include="Collections\EnumerableExtensions.Page.cs" />
Expand Down
30 changes: 30 additions & 0 deletions Main/src/Collections/DisjointSets.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
namespace CodeJam.Collections
{
/// <summary>Disjoint sets without payload</summary>
/// <remarks>
/// See http://en.wikipedia.org/wiki/Disjoint-set_data_structure
/// </remarks>
public sealed class DisjointSets : DisjointSetsBase<BasicNode>
{
/// <summary>Creates an empty Disjoint sets</summary>
public DisjointSets() { }

/// <summary>Creates a Disjoint sets with the given number of elements</summary>
/// <param name="count">The initial number of elements</param>
public DisjointSets(int count)
{
Add(count);
}

/// <summary>Appends the given number of new elements</summary>
/// <param name="count">The number of elements to add</param>
public void Add(int count)
{
for (var i = 0; i < count; ++i)
{
Nodes_.Add(new BasicNode { ParentIndex = -1, Rank = 0 });
}
SetsCount += count;
}
}
}
104 changes: 104 additions & 0 deletions Main/src/Collections/DisjointSetsBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
using System.Collections.Generic;

namespace CodeJam.Collections
{
/// <summary>Disjoint sets implementation base</summary>
/// <remarks>
/// See http://en.wikipedia.org/wiki/Disjoint-set_data_structure
/// </remarks>
public class DisjointSetsBase<T> where T : BasicNode
{
/// <summary>All nodes</summary>
protected readonly List<T> Nodes_ = new List<T>();

/// <summary>Creates an empty base</summary>
protected DisjointSetsBase() { }

/// <summary>The number of nodes</summary>
public int Count => Nodes_.Count;

/// <summary>The number of disjoint sets</summary>
public int SetsCount { get; protected set; }

/// <summary>Finds a set identifier for the element</summary>
/// <param name="index">The element index</param>
/// <returns>The identifier of the containing set</returns>
/// <remarks>
/// The set identifier is the index of a single element representing the set.
/// The Union operation may lead to a choice of a different representative for a set.
/// In this case IndexToSetId(oldSetId) may be called to get the new set id.
/// </remarks>
public int IndexToSetId(int index)
{
// First, find a root element of a tree containing the passed element
var rootIndex = index;
for (;;)
{
var parentIndex = Nodes_[rootIndex].ParentIndex;
if (parentIndex == -1)
{
break;
}
rootIndex = parentIndex;
}

// Then, do the path compression:
// walk from the passed element upto the root replacing the the ParentIndex with the root index
while (index != rootIndex)
{
var node = Nodes_[index];
index = node.ParentIndex;
node.ParentIndex = rootIndex;
}
return rootIndex;
}

/// <summary>Combines to distjoint sets into a single set</summary>
/// <param name="elementOfSet1Index">Index of an element of the first set</param>
/// <param name="elementOfSet2Index">Index of an element of the second set</param>
public void Union(int elementOfSet1Index, int elementOfSet2Index)
{
elementOfSet1Index = IndexToSetId(elementOfSet1Index);
elementOfSet2Index = IndexToSetId(elementOfSet2Index);
if (elementOfSet1Index == elementOfSet2Index)
{
return; // Already the single set
}

var set1Root = Nodes_[elementOfSet1Index];
var set2Root = Nodes_[elementOfSet2Index];
var rankDifference = set1Root.Rank - set2Root.Rank;
// Attach the tree with a smaller rank to the tree with a higher rank.
// The resulting tree rank is equal to the higher rank
// except the case when initial ranks are equal.
// In the latter case the new rank will be increased by 1
if (rankDifference > 0) // 1st has higher rank
{
set2Root.ParentIndex = elementOfSet1Index;
}
else if (rankDifference < 0) // 2nd has the higher rank
{
set1Root.ParentIndex = elementOfSet2Index;
}
else // ranks are equal and the new root choice is arbitrary
{
set2Root.ParentIndex = elementOfSet1Index;
++set1Root.Rank;
}

// we have joined 2 sets, so we have to decrease the count
--SetsCount;
}
}

/// <summary>Node base class</summary>
public class BasicNode
{
/// <summary>Parent node index</summary>
/// <remarks>Points to the root after a path compression</remarks>
public int ParentIndex;

/// <summary>Estimated height of the tree (i.e. maximum length of the path from the root to a node. Path compression is not taken into account)</summary>
public int Rank;
}
}
52 changes: 52 additions & 0 deletions Main/src/Collections/DisjointSetsT.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using System.Collections.Generic;
using System.Linq;

namespace CodeJam.Collections
{
/// <summary>
/// Generic implementation of the Disjoint sets
/// </summary>
/// <remarks>
/// See http://en.wikipedia.org/wiki/Disjoint-set_data_structure
/// </remarks>
public sealed class DisjointSets<T> : DisjointSetsBase<DisjointSets<T>.Node>
{
/// <summary>Creates an empty Disjoint sets</summary>
public DisjointSets() { }

/// <summary>Creates a Disjoint sets with the passed values</summary>
/// <param name="values">The values to store</param>
public DisjointSets(IEnumerable<T> values)
{
Add(values);
}

/// <summary>Gets an element by its index</summary>
/// <param name="index">Elmement's index</param>
public T this[int index] => Nodes_[index].Value;

/// <summary>Appends a list of values</summary>
/// <param name="values">The values to append</param>
public void Add(IEnumerable<T> values)
{
var initialNodesCount = Nodes_.Count;
Nodes_.AddRange(values.Select(_ => new Node { Value = _, ParentIndex = -1, Rank = 0 }));
SetsCount += Nodes_.Count - initialNodesCount;
}

/// <summary>Appends a single element</summary>
/// <param name="value">The value to append</param>
public void Add(T value)
{
Nodes_.Add(new Node { Value = value, ParentIndex = -1, Rank = 0 });
++SetsCount;
}

/// <summary>A sets node</summary>
public class Node : BasicNode
{
/// <summary>The node data</summary>
public T Value;
}
}
}
1 change: 1 addition & 0 deletions Main/tests/CodeJam.Main-Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
<Compile Include="Assertions\CodeAssertionsTest.cs" />
<Compile Include="Collections\AggregateFuncsTest.cs" />
<Compile Include="Collections\ArrayExtensionsTest.cs" />
<Compile Include="Collections\DisjointSetsTest.cs" />
<Compile Include="Collections\QueryableExtensionsTests.ApplyOrder.cs" />
<Compile Include="Collections\DictionaryExtensionsTest.cs" />
<Compile Include="Collections\AggregateFuncsTest.generated.cs">
Expand Down
65 changes: 65 additions & 0 deletions Main/tests/Collections/DisjointSetsTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.Linq;

using CodeJam.Collections;

using NUnit.Framework;

namespace Tests
{
[TestFixture]
public class DisjointSetsTest
{
private readonly Random random_ = new Random();
private const int ElementsNumber = 10000;
private readonly List<int> seq_ = Enumerable.Range(0, ElementsNumber).ToList();

[Test]
public void Test01NonGeneric()
{
for (var i = 1; i <= ElementsNumber; i += 1 + i / (10 + random_.Next(0, 10)))
{
Console.WriteLine("i = {0}", i);
var djs = new DisjointSets(ElementsNumber);
foreach (var el in RandomShuffle(seq_))
{
djs.Union(el, el % i);
}
VerifySets(djs, i);
}
}

[Test]
public void Test02Generic()
{
for (var i = 1; i <= ElementsNumber; i += 1 + i / (10 + random_.Next(0, 10)))
{
Console.WriteLine("i = {0}", i);
var rs = RandomShuffle(seq_).ToList();
var djs = new DisjointSets<int>(rs);
foreach (var el in rs)
{
djs.Union(el, el % i);
}
VerifySets(djs, i);
for (var j = 0; j < ElementsNumber; ++j)
{
Assert.That(djs[j], Is.EqualTo(rs[j]));
}
}
}

private static void VerifySets<T>(DisjointSetsBase<T> djs, int mod) where T : BasicNode
{
Assert.That(djs.Count, Is.EqualTo(ElementsNumber));
Assert.That(djs.SetsCount, Is.EqualTo(mod));
for (var i = 0; i < ElementsNumber; ++i)
{
Assert.That(djs.IndexToSetId(i), Is.EqualTo(djs.IndexToSetId(i % mod)), "i = {0}, mod = {1}", i, mod);
}
}

private IEnumerable<T> RandomShuffle<T>(IEnumerable<T> en) => en.OrderBy(x => random_.Next());
}
}