You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
167 lines
3.6 KiB
167 lines
3.6 KiB
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Runtime.CompilerServices;
|
|
|
|
public class SimpleHeap<T> : IEnumerable<T>
|
|
{
|
|
const int ROOT_IDX = 0;
|
|
const int INVALID_IDX = -1;
|
|
|
|
T[] data = new T[0];
|
|
int count = 0;
|
|
Func<T, T, bool> m_compareFunc;
|
|
|
|
public int Count => count;
|
|
|
|
public SimpleHeap(Func<T, T, bool> compareFunc)
|
|
{
|
|
m_compareFunc = compareFunc;
|
|
}
|
|
|
|
public SimpleHeap(T[] collection, Func<T, T, bool> compareFunc)
|
|
{
|
|
m_compareFunc = compareFunc;
|
|
data = new T[collection.Length];
|
|
|
|
for (int i = 0; i < collection.Length; i++)
|
|
{
|
|
Push(collection[i]);
|
|
}
|
|
}
|
|
|
|
public void Push(T value)
|
|
{
|
|
insertBack(value);
|
|
climbUntilLimit(count - 1);
|
|
}
|
|
|
|
public T Pop()
|
|
{
|
|
if (IsEmpty())
|
|
return default;
|
|
|
|
T result = data[ROOT_IDX];
|
|
swap(0, --count);
|
|
fallUntilLimit(0);
|
|
|
|
return result;
|
|
}
|
|
|
|
public T GetRootValue()
|
|
{
|
|
if (IsEmpty())
|
|
return default;
|
|
|
|
return data[ROOT_IDX];
|
|
}
|
|
|
|
public bool TryGetRootValue(out T value)
|
|
{
|
|
if (IsEmpty())
|
|
{
|
|
value = default;
|
|
return false;
|
|
}
|
|
|
|
value = data[ROOT_IDX];
|
|
return true;
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public bool IsEmpty() => count == 0;
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public void Clear() => count = 0;
|
|
|
|
void climbUntilLimit(int curIdx)
|
|
{
|
|
int parentIdx = getParentIdx(curIdx);
|
|
|
|
if (parentIdx == INVALID_IDX)
|
|
return;
|
|
|
|
if (m_compareFunc(data[curIdx], data[parentIdx]))
|
|
{
|
|
swap(curIdx, parentIdx);
|
|
climbUntilLimit(parentIdx);
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
void fallUntilLimit(int curIdx)
|
|
{
|
|
int leftChildIdx = getLeftChildIdx(curIdx);
|
|
int rightChildIdx = getRightChildIdx(curIdx);
|
|
|
|
int aptIdx = curIdx;
|
|
if (leftChildIdx != INVALID_IDX && rightChildIdx != INVALID_IDX)
|
|
{
|
|
aptIdx = getAptIndex(leftChildIdx, rightChildIdx);
|
|
aptIdx = getAptIndex(curIdx, aptIdx);
|
|
}
|
|
else if (leftChildIdx != INVALID_IDX)
|
|
{
|
|
aptIdx = getAptIndex(curIdx, leftChildIdx);
|
|
}
|
|
|
|
if (aptIdx == curIdx)
|
|
{
|
|
return;
|
|
}
|
|
|
|
swap(curIdx, aptIdx);
|
|
fallUntilLimit(aptIdx);
|
|
}
|
|
|
|
void insertBack(T value)
|
|
{
|
|
if (count >= data.Length)
|
|
Array.Resize(ref data, count + 1);
|
|
|
|
data[count++] = value;
|
|
}
|
|
|
|
int getParentIdx(int curIdx)
|
|
{
|
|
if (curIdx == 0)
|
|
return -1;
|
|
|
|
return (int)((curIdx - 1) * 0.5f);
|
|
}
|
|
|
|
int getLeftChildIdx(int curIdx)
|
|
{
|
|
int result = curIdx * 2 + 1;
|
|
|
|
if (result >= count)
|
|
result = INVALID_IDX;
|
|
|
|
return result;
|
|
}
|
|
|
|
int getRightChildIdx(int curIdx)
|
|
{
|
|
int result = curIdx * 2 + 2;
|
|
|
|
if (result >= count)
|
|
result = INVALID_IDX;
|
|
|
|
return result;
|
|
}
|
|
|
|
int getAptIndex(int value1Index, int value2Index) => m_compareFunc(data[value1Index], data[value2Index]) ? value1Index : value2Index;
|
|
|
|
void swap(int sourceIdx, int destinationIdx)
|
|
{
|
|
T tmp = data[sourceIdx];
|
|
data[sourceIdx] = data[destinationIdx];
|
|
data[destinationIdx] = tmp;
|
|
}
|
|
|
|
public IEnumerator<T> GetEnumerator() => ((IEnumerable<T>)data).GetEnumerator();
|
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
|
}
|