Skip to content

Commit

Permalink
add binary heap implementation with support for siftdown optimization
Browse files Browse the repository at this point in the history
  • Loading branch information
eiriktsarpalis committed Feb 9, 2021
1 parent fe7e10c commit 1670553
Show file tree
Hide file tree
Showing 9 changed files with 790 additions and 84 deletions.
161 changes: 93 additions & 68 deletions PriorityQueue.Benchmarks/HeapBenchmarks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public class HeapBenchmarks
private int[] _priorities;
private PriorityQueue<int> _priorityQueue2;
private PriorityQueue<int, int> _priorityQueue;
private PriorityQueue_Binary<int, int> _priorityQueueBinary;
private PriorityQueue_Comparable<int, int> _priorityQueueComparable;
private PriorityQueue_InlineComparer<int, int> _priorityQueueInlineComparer;
private PrioritySet<int, int> _prioritySet;
Expand All @@ -30,6 +31,7 @@ public void Initialize()
_priorities[i] = random.Next();
}

_priorityQueueBinary = new PriorityQueue_Binary<int, int>(initialCapacity: Size);
_priorityQueueComparable = new PriorityQueue_Comparable<int, int>(initialCapacity: Size);
_priorityQueueInlineComparer = new PriorityQueue_InlineComparer<int, int>(initialCapacity: Size);
_priorityQueue2 = new PriorityQueue<int>(initialCapacity: Size);
Expand Down Expand Up @@ -61,6 +63,29 @@ public void PriorityQueue()
}
}

[Benchmark]
public void PriorityQueue_Binary()
{
var queue = _priorityQueueBinary;
var priorities = _priorities;

for (int i = 0; i < Size; i++)
{
queue.Enqueue(i, priorities[i]);
}

for (int i = Size; i < 2 * Size; i++)
{
queue.Dequeue();
queue.Enqueue(i, priorities[i]);
}

while (queue.Count > 0)
{
queue.Dequeue();
}
}

[Benchmark]
public void PriorityQueue_InlineComparer()
{
Expand Down Expand Up @@ -107,73 +132,73 @@ public void PriorityQueue_Comparable()
}
}

//[Benchmark]
//public void PriorityQueue2()
//{
// var queue = _priorityQueue2;
// var priorities = _priorities;

// for (int i = 0; i < Size; i++)
// {
// queue.Enqueue(priorities[i]);
// }

// for (int i = Size; i < 2 * Size; i++)
// {
// queue.Dequeue();
// queue.Enqueue(priorities[i]);
// }

// while (queue.Count > 0)
// {
// queue.Dequeue();
// }
//}

//[Benchmark]
//public void PrioritySet()
//{
// var queue = _prioritySet;
// var priorities = _priorities;

// for (int i = 0; i < Size; i++)
// {
// queue.Enqueue(i, priorities[i]);
// }

// for (int i = Size; i < 2 * Size; i++)
// {
// queue.Dequeue();
// queue.Enqueue(i, priorities[i]);
// }

// while (queue.Count > 0)
// {
// queue.Dequeue();
// }
//}

//[Benchmark]
//public void PairingHeap()
//{
// var heap = _pairingHeap;
// var priorities = _priorities;

// for (int i = 0; i < Size; i++)
// {
// heap.Add(priorities[i], i);
// }

// for (int i = Size; i < 2 * Size; i++)
// {
// heap.Pop();
// heap.Add(priorities[i], i);
// }

// while (heap.Count > 0)
// {
// heap.Pop();
// }
//}
[Benchmark]
public void PriorityQueue2()
{
var queue = _priorityQueue2;
var priorities = _priorities;

for (int i = 0; i < Size; i++)
{
queue.Enqueue(priorities[i]);
}

for (int i = Size; i < 2 * Size; i++)
{
queue.Dequeue();
queue.Enqueue(priorities[i]);
}

while (queue.Count > 0)
{
queue.Dequeue();
}
}

[Benchmark]
public void PrioritySet()
{
var queue = _prioritySet;
var priorities = _priorities;

for (int i = 0; i < Size; i++)
{
queue.Enqueue(i, priorities[i]);
}

for (int i = Size; i < 2 * Size; i++)
{
queue.Dequeue();
queue.Enqueue(i, priorities[i]);
}

while (queue.Count > 0)
{
queue.Dequeue();
}
}

[Benchmark]
public void PairingHeap()
{
var heap = _pairingHeap;
var priorities = _priorities;

for (int i = 0; i < Size; i++)
{
heap.Add(priorities[i], i);
}

for (int i = Size; i < 2 * Size; i++)
{
heap.Pop();
heap.Add(priorities[i], i);
}

while (heap.Count > 0)
{
heap.Pop();
}
}
}
}
8 changes: 4 additions & 4 deletions PriorityQueue.Tests/PriorityQueueTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public static void Simple_Priority_Queue()
pq.Enqueue("John", 1940);
pq.Enqueue("Paul", 1942);
pq.Enqueue("George", 1943);
pq.Enqueue("Ringo", 1940);
pq.Enqueue("Ringo", 1941);

Assert.Equal("John", pq.Dequeue());
Assert.Equal("Ringo", pq.Dequeue());
Expand All @@ -27,7 +27,7 @@ public static void Simple_Priority_Queue()
[Fact]
public static void Simple_Priority_Queue_Heapify()
{
var pq = new PriorityQueue<string, int>(new (string, int)[] { ("John", 1940), ("Paul", 1942), ("George", 1943), ("Ringo", 1940) });
var pq = new PriorityQueue<string, int>(new (string, int)[] { ("John", 1940), ("Paul", 1942), ("George", 1943), ("Ringo", 1941) });

Assert.Equal("John", pq.Dequeue());
Assert.Equal("Ringo", pq.Dequeue());
Expand All @@ -38,9 +38,9 @@ public static void Simple_Priority_Queue_Heapify()
[Fact]
public static void Simple_Priority_Queue_Enumeration()
{
var pq = new PriorityQueue<string, int>(new (string, int)[] { ("John", 1940), ("Paul", 1942), ("George", 1943), ("Ringo", 1940) });
var pq = new PriorityQueue<string, int>(new (string, int)[] { ("John", 1940), ("Paul", 1942), ("George", 1943), ("Ringo", 1941) });

(string, int)[] expected = new[] { ("John", 1940), ("Paul", 1942), ("George", 1943), ("Ringo", 1940) };
(string, int)[] expected = new[] { ("John", 1940), ("Paul", 1942), ("George", 1943), ("Ringo", 1941) };
Assert.Equal(expected, pq.UnorderedItems.ToArray());
}

Expand Down
104 changes: 104 additions & 0 deletions PriorityQueue.Tests/PriorityQueue_Binary.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
using System;
using Xunit;
using FsCheck.Xunit;
using System.Collections.Generic;
using System.Linq;

namespace PriorityQueue.Tests
{
public class PriorityQueue_BinaryTests
{
[Fact]
public static void Simple_Priority_Queue()
{
var pq = new PriorityQueue_Binary<string, int>();

pq.Enqueue("John", 1940);
pq.Enqueue("Paul", 1942);
pq.Enqueue("George", 1943);
pq.Enqueue("Ringo", 1941);

Assert.Equal("John", pq.Dequeue());
Assert.Equal("Ringo", pq.Dequeue());
Assert.Equal("Paul", pq.Dequeue());
Assert.Equal("George", pq.Dequeue());
}

[Fact]
public static void Simple_Priority_Queue_Heapify()
{
var pq = new PriorityQueue_Binary<string, int>(new (string, int)[] { ("John", 1940), ("Paul", 1942), ("George", 1943), ("Ringo", 1941) });

Assert.Equal("John", pq.Dequeue());
Assert.Equal("Ringo", pq.Dequeue());
Assert.Equal("Paul", pq.Dequeue());
Assert.Equal("George", pq.Dequeue());
}

[Fact]
public static void Simple_Priority_Queue_Enumeration()
{
var pq = new PriorityQueue_Binary<string, int>(new (string, int)[] { ("John", 1940), ("Paul", 1942), ("George", 1943), ("Ringo", 1941) });

(string, int)[] expected = new[] { ("John", 1940), ("Ringo", 1941), ("George", 1943), ("Paul", 1942) };
Assert.Equal(expected, pq.UnorderedItems.ToArray());
}

[Property(MaxTest = 10_000)]
public static void HeapSort_Should_Work(string[] inputs)
{
string[] expected = inputs.OrderBy(inp => inp).ToArray();
string[] actual = HeapSort(inputs).ToArray();
Assert.Equal(expected, actual);

static IEnumerable<string> HeapSort(string[] inputs)
{
var pq = new PriorityQueue_Binary<string, string>();
ValidateState(pq);

foreach (string input in inputs)
{
pq.Enqueue(input, input);
ValidateState(pq);
}

Assert.Equal(inputs.Length, pq.Count);

while (pq.Count > 0)
{
yield return pq.Dequeue();
ValidateState(pq);
}
}
}

[Property(MaxTest = 10_000)]
public static void HeapSort_Ctor_Should_Work(string[] inputs)
{
string[] expected = inputs.OrderBy(inp => inp).ToArray();
string[] actual = HeapSort(inputs).ToArray();
Assert.Equal(expected, actual);

static IEnumerable<string> HeapSort(string[] inputs)
{
var pq = new PriorityQueue_Binary<string, string>(inputs.Select(x => (x, x)));

ValidateState(pq);
Assert.Equal(inputs.Length, pq.Count);

while (pq.Count > 0)
{
yield return pq.Dequeue();
ValidateState(pq);
}
}
}

private static void ValidateState<TElement, TPriority>(PriorityQueue_Binary<TElement, TPriority> pq)
{
#if DEBUG
pq.ValidateInternalState();
#endif
}
}
}
8 changes: 4 additions & 4 deletions PriorityQueue.Tests/PriorityQueue_ComparableTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public static void Simple_Priority_Queue()
pq.Enqueue("John", 1940);
pq.Enqueue("Paul", 1942);
pq.Enqueue("George", 1943);
pq.Enqueue("Ringo", 1940);
pq.Enqueue("Ringo", 1941);

Assert.Equal("John", pq.Dequeue());
Assert.Equal("Ringo", pq.Dequeue());
Expand All @@ -28,7 +28,7 @@ public static void Simple_Priority_Queue()
[Fact]
public static void Simple_Priority_Queue_Heapify()
{
var pq = new PriorityQueue_Comparable<string, int>(new (string, int)[] { ("John", 1940), ("Paul", 1942), ("George", 1943), ("Ringo", 1940) });
var pq = new PriorityQueue_Comparable<string, int>(new (string, int)[] { ("John", 1940), ("Paul", 1942), ("George", 1943), ("Ringo", 1941) });

Assert.Equal("John", pq.Dequeue());
Assert.Equal("Ringo", pq.Dequeue());
Expand All @@ -39,9 +39,9 @@ public static void Simple_Priority_Queue_Heapify()
[Fact]
public static void Simple_Priority_Queue_Enumeration()
{
var pq = new PriorityQueue_Comparable<string, int>(new (string, int)[] { ("John", 1940), ("Paul", 1942), ("George", 1943), ("Ringo", 1940) });
var pq = new PriorityQueue_Comparable<string, int>(new (string, int)[] { ("John", 1940), ("Paul", 1942), ("George", 1943), ("Ringo", 1941) });

(string, int)[] expected = new[] { ("John", 1940), ("Paul", 1942), ("George", 1943), ("Ringo", 1940) };
(string, int)[] expected = new[] { ("John", 1940), ("Paul", 1942), ("George", 1943), ("Ringo", 1941) };
Assert.Equal(expected, pq.UnorderedItems.ToArray());
}

Expand Down
8 changes: 4 additions & 4 deletions PriorityQueue.Tests/PriorityQueue_InlineComparerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public static void Simple_Priority_Queue()
pq.Enqueue("John", 1940);
pq.Enqueue("Paul", 1942);
pq.Enqueue("George", 1943);
pq.Enqueue("Ringo", 1940);
pq.Enqueue("Ringo", 1941);

Assert.Equal("John", pq.Dequeue());
Assert.Equal("Ringo", pq.Dequeue());
Expand All @@ -27,7 +27,7 @@ public static void Simple_Priority_Queue()
[Fact]
public static void Simple_Priority_Queue_Heapify()
{
var pq = new PriorityQueue_InlineComparer<string, int>(new (string, int)[] { ("John", 1940), ("Paul", 1942), ("George", 1943), ("Ringo", 1940) });
var pq = new PriorityQueue_InlineComparer<string, int>(new (string, int)[] { ("John", 1940), ("Paul", 1942), ("George", 1943), ("Ringo", 1941) });

Assert.Equal("John", pq.Dequeue());
Assert.Equal("Ringo", pq.Dequeue());
Expand All @@ -38,9 +38,9 @@ public static void Simple_Priority_Queue_Heapify()
[Fact]
public static void Simple_Priority_Queue_Enumeration()
{
var pq = new PriorityQueue_InlineComparer<string, int>(new (string, int)[] { ("John", 1940), ("Paul", 1942), ("George", 1943), ("Ringo", 1940) });
var pq = new PriorityQueue_InlineComparer<string, int>(new (string, int)[] { ("John", 1940), ("Paul", 1942), ("George", 1943), ("Ringo", 1941) });

(string, int)[] expected = new[] { ("John", 1940), ("Paul", 1942), ("George", 1943), ("Ringo", 1940) };
(string, int)[] expected = new[] { ("John", 1940), ("Paul", 1942), ("George", 1943), ("Ringo", 1941) };
Assert.Equal(expected, pq.UnorderedItems.ToArray());
}

Expand Down
Loading

0 comments on commit 1670553

Please sign in to comment.