Skip to content

Instantly share code, notes, and snippets.

@tkokof
Created February 2, 2019 12:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tkokof/11981af32f993ee28466ad068862ff79 to your computer and use it in GitHub Desktop.
Save tkokof/11981af32f993ee28466ad068862ff79 to your computer and use it in GitHub Desktop.
generic heap implementation
using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Collections.ObjectModel;
public class Heap<T> where T : IEquatable<T>
{
public static Heap<T> Build(IEnumerable<T> items, Func<T, T, bool> predicate)
{
var heap = new Heap<T>(predicate);
if (items != null)
{
var iter = items.GetEnumerator();
while (iter.MoveNext())
{
var item = iter.Current;
heap.Add(item);
}
}
return heap;
}
/*
public static Heap<T> Build2(IEnumerable<T> items, Func<T, T, bool> predicate)
{
var heap = new Heap<T>(predicate);
if (items != null)
{
heap.m_items.AddRange(items);
var itemCountIndexBegin = heap.m_items.Count / 2;
var itemCountIndexEnd = 1;
for (int i = itemCountIndexBegin; i >= itemCountIndexEnd; --i)
{
// NOTE this requires Heapify() just do "down-heap" operation
// now Heapify() do both "up-heap" and "down-heap" operation
heap.Heapify(i - 1);
}
}
return heap;
}
*/
Func<T, T, bool> m_predicate;
List<T> m_items = new List<T>();
public int Count
{
get
{
return m_items.Count;
}
}
public ReadOnlyCollection<T> Items
{
get
{
return m_items.AsReadOnly();
}
}
public T this[int index]
{
get
{
return m_items[index];
}
}
public Heap(Func<T, T, bool> predicate)
{
Debug.Assert(predicate != null);
m_predicate = predicate;
}
int IndexOfRecur(T item, int itemIndex)
{
if (IsValidIndex(itemIndex))
{
if (!m_predicate(item, m_items[itemIndex]))
{
// not found
return -1;
}
else
{
if (m_items[itemIndex].Equals(item))
{
return itemIndex;
}
else
{
var itemCountIndex = itemIndex + 1;
var childCountIndexLeft = itemCountIndex * 2;
var childIndexLeft = childCountIndexLeft - 1;
var index = IndexOfRecur(item, childIndexLeft);
if (index >= 0)
{
return index;
}
else
{
var childCountIndexRight = itemCountIndex * 2 + 1;
var childIndexRight = childCountIndexRight - 1;
return IndexOfRecur(item, childIndexRight);
}
}
}
}
// default return -1
return -1;
}
// return -1 when not found(recur)
public int IndexOf(T item)
{
return IndexOfRecur(item, 0);
}
// return -1 when not found(iterator)
public int IndexOf2(T item)
{
return m_items.IndexOf(item);
}
public void Add(T item)
{
m_items.Add(item);
Heapify(m_items.Count - 1);
}
public void Remove(T item)
{
var itemIndex = IndexOf(item);
if (itemIndex >= 0)
{
Swap(itemIndex, m_items.Count - 1);
m_items.RemoveAt(m_items.Count - 1);
Heapify(itemIndex);
}
}
public void RemoveAt(int itemIndex)
{
if (IsValidIndex(itemIndex))
{
Swap(itemIndex, m_items.Count - 1);
m_items.RemoveAt(m_items.Count - 1);
Heapify(itemIndex);
}
}
public void Clear()
{
m_items.Clear();
}
bool IsValidIndex(int itemIndex)
{
return itemIndex >= 0 && itemIndex < m_items.Count;
}
void Swap(int index1, int index2)
{
if (IsValidIndex(index1) && IsValidIndex(index2))
{
var temp = m_items[index1];
m_items[index1] = m_items[index2];
m_items[index2] = temp;
}
}
void Heapify(int itemIndex)
{
if (IsValidIndex(itemIndex))
{
var itemCountIndex = itemIndex + 1;
// first check parent
var parentCountIndex = itemCountIndex / 2;
var parentIndex = parentCountIndex - 1;
if (IsValidIndex(parentIndex) && !m_predicate(m_items[itemIndex], m_items[parentIndex]))
{
while (IsValidIndex(itemIndex) && IsValidIndex(parentIndex) && !m_predicate(m_items[itemIndex], m_items[parentIndex]))
{
Swap(itemIndex, parentIndex);
// update index
itemIndex = parentIndex;
itemCountIndex = itemIndex + 1;
parentCountIndex = itemCountIndex / 2;
parentIndex = parentCountIndex - 1;
}
}
else
{
// then check child
var childCountIndexLeft = itemCountIndex * 2;
var childIndexLeft = childCountIndexLeft - 1;
var childCountIndexRight = itemCountIndex * 2 + 1;
var childIndexRight = childCountIndexRight - 1;
while (IsValidIndex(childIndexLeft))
{
if (IsValidIndex(childIndexRight))
{
if (!m_predicate(m_items[childIndexLeft], m_items[itemIndex]) || !m_predicate(m_items[childIndexRight], m_items[itemIndex]))
{
if (!m_predicate(m_items[childIndexLeft], m_items[itemIndex]) && m_predicate(m_items[childIndexRight], m_items[itemIndex]))
{
Swap(childIndexLeft, itemIndex);
itemIndex = childIndexLeft;
itemCountIndex = childCountIndexLeft;
}
else if (m_predicate(m_items[childIndexLeft], m_items[itemIndex]) && !m_predicate(m_items[childIndexRight], m_items[itemIndex]))
{
Swap(childIndexRight, itemIndex);
itemIndex = childIndexRight;
itemCountIndex = childCountIndexRight;
}
else
{
if (m_predicate(m_items[childIndexLeft], m_items[childIndexRight]))
{
// left should be child
Swap(childIndexRight, itemIndex);
itemIndex = childIndexRight;
itemCountIndex = childCountIndexRight;
}
else
{
// right should be child
Swap(childIndexLeft, itemIndex);
itemIndex = childIndexLeft;
itemCountIndex = childCountIndexLeft;
}
}
// update index
childCountIndexLeft = itemCountIndex * 2;
childIndexLeft = childCountIndexLeft - 1;
childCountIndexRight = itemCountIndex * 2 + 1;
childIndexRight = childCountIndexRight - 1;
}
else
{
// break since fit
break;
}
}
else
{
// right child is invalid, reach end
if (!m_predicate(m_items[childIndexLeft], m_items[itemIndex]))
{
Swap(childIndexLeft, itemIndex);
}
// break since reach end
break;
}
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment