As follows I have implemented Min and Max heap data structure, using an array for storing elements. I would need a code review, please. Please, spare recommendations for generic implementation as they are not useful in my current requirements. I have written down units tests and I'm sharing those, too.
Abstract Class
public abstract class AbstractHeap
{
#region internal properties
private int Capacity { get; set; }
internal int Size { get; set; }
internal int[] Nodes { get; set; }
#endregion
#region constructors
public AbstractHeap()
{
Capacity = 100;
Size = 0;
Nodes = new int[Capacity];
}
#endregion
#region helperMethods
public void EnlargeIfNeeded()
{
if (Size == Capacity)
{
Capacity = 2 * Capacity;
Array.Copy(Nodes, Nodes, Capacity);
}
}
public int getLeftChildIndex(int parentIndex)
{
return 2 * parentIndex + 1;
}
public bool hasLeftChild(int parentIndex)
{
return getLeftChildIndex(parentIndex) < Size;
}
public int leftChild(int index)
{
return Nodes[getLeftChildIndex(index)];
}
public int getRightChildIndex(int parentIndex)
{
return 2 * parentIndex + 2;
}
public bool hasRightChild(int parentIndex)
{
return getRightChildIndex(parentIndex) < Size;
}
public int rightChild(int index)
{
return Nodes[getRightChildIndex(index)];
}
public int getParentIndex(int childIndex)
{
return (childIndex - 1) / 2;
}
public bool hasParent(int childIndex)
{
return getParentIndex(childIndex) >= 0;
}
public int parent(int index)
{
return Nodes[getParentIndex(index)];
}
public void swap(int index1, int index2)
{
int temp = Nodes[index1];
Nodes[index1] = Nodes[index2];
Nodes[index2] = temp;
}
#endregion
#region available public methods
/// <summary>
/// Gets the minimum element at the root of the tree
/// </summary>
/// <returns>Int value of minimum element</returns>
/// <exception cref="">InvalidOperationException when heap is empty</exception>
public int peek()
{
if (Size == 0)
throw new InvalidOperationException("Heap is empty");
return Nodes[0];
}
/// <summary>
/// Removes the minimum element at the root of the tree
/// </summary>
/// <returns>Int value of minimum element</returns>
/// <exception cref="">InvalidOperationException when heap is empty</exception>
public int pop()
{
if (Size == 0)
throw new InvalidOperationException("Heap is empty");
int item = Nodes[0];
Nodes[0] = Nodes[Size - 1];
Size--;
heapifyDown();
return item;
}
/// <summary>
/// Add a new item to heap, enlarging the array if needed
/// </summary>
/// <returns>void</returns>
public void add(int item)
{
EnlargeIfNeeded();
Nodes[Size] = item;
Size++;
heapifyUp();
}
#endregion
#region abstract methods
internal abstract void heapifyUp();
internal abstract void heapifyDown();
#endregion
}
Max Heap implementation using abstract class
public class MaxHeap : AbstractHeap
{
#region constructors
public MaxHeap() : base()
{
}
#endregion
#region internal methods
internal override void heapifyDown()
{
int index = 0;
while (hasLeftChild(index))
{
int largerChildIndex = getLeftChildIndex(index);
if (hasRightChild(index) && rightChild(index) > leftChild(index))
{
largerChildIndex = getRightChildIndex(index);
}
if (Nodes[largerChildIndex] > Nodes[index])
swap(index, largerChildIndex);
else
break;
index = largerChildIndex;
}
}
internal override void heapifyUp()
{
int index = Size - 1;
while (hasParent(index) && parent(index) < Nodes[index])
{
swap(index, getParentIndex(index));
index = getParentIndex(index);
}
}
#endregion
}
Min Heap implementation using abstract class
public class MinHeap : AbstractHeap
{
#region constructors
public MinHeap() : base()
{
}
#endregion
#region internal methods
internal override void heapifyDown()
{
int index = 0;
while (hasLeftChild(index))
{
int smallerChildIndex = getLeftChildIndex(index);
if (hasRightChild(index) && rightChild(index) < leftChild(index))
{
smallerChildIndex = getRightChildIndex(index);
}
if (Nodes[smallerChildIndex] < Nodes[index])
swap(index, smallerChildIndex);
else
break;
index = smallerChildIndex;
}
}
internal override void heapifyUp()
{
int index = Size - 1;
while (hasParent(index) && parent(index) > Nodes[index])
{
swap(index, getParentIndex(index));
index = getParentIndex(index);
}
}
#endregion
}
Unit tests for max heap
[TestClass]
public class MaxHeapTests
{
[TestMethod]
public void AddEmptyRemove()
{
var heap = new MaxHeap();
heap.add(10);
Assert.AreEqual(10, heap.peek());
int result = heap.pop();
Assert.AreEqual(10, result);
heap.add(20);
Assert.AreEqual(20, heap.peek());
}
[TestMethod]
public void AddMultipleCheckPeek()
{
var heap = new MaxHeap();
foreach (int item in new int[] { 10, 20, 2, 45, 7, 5, 12 })
heap.add(item);
Assert.AreEqual(heap.peek(), 45);
}
[TestMethod]
public void AddMultipleCheckPopThenPeek()
{
var heap = new MaxHeap();
foreach (int item in new int[] { 10, 20, 2, 45, 7, 5, 12 })
heap.add(item);
int result = heap.pop();
Assert.AreEqual(45, result);
Assert.AreEqual(20, heap.peek());
}
[TestMethod]
[ExpectedException(typeof(InvalidOperationException))]
public void PeekPoopEmpty()
{
var heap = new MaxHeap();
heap.peek();
heap.pop();
}
}
Unit tests for min heap
[TestClass]
public class MinHeapTests
{
[TestMethod]
public void AddEmptyRemove()
{
var heap = new MinHeap();
heap.add(10);
Assert.AreEqual(10, heap.peek());
int result = heap.pop();
Assert.AreEqual(10, result);
heap.add(20);
Assert.AreEqual(20, heap.peek());
}
[TestMethod]
public void AddMultipleCheckPeek()
{
var heap = new MinHeap();
foreach (int item in new int[] { 10, 20, 2, 45, 7, 5, 12 })
heap.add(item);
Assert.AreEqual(heap.peek(), 2);
}
[TestMethod]
public void AddMultipleCheckPopThenPeek()
{
var heap = new MinHeap();
foreach (int item in new int[] { 10, 20, 2, 45, 7, 5, 12 })
heap.add(item);
int result = heap.pop();
Assert.AreEqual(2, result);
Assert.AreEqual(5, heap.peek());
}
[TestMethod]
[ExpectedException(typeof(InvalidOperationException))]
public void PeekPoopEmpty()
{
var heap = new MinHeap();
heap.peek();
heap.pop();
}
}