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.
158 lines
3.3 KiB
158 lines
3.3 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using System.Runtime.CompilerServices;
|
|
using UnityEngine;
|
|
|
|
public class SimpleHeap<T>
|
|
{
|
|
const int ROOT_IDX = 0;
|
|
const int INVALID_IDX = -1;
|
|
|
|
T[] data = new T[0];
|
|
int count = 0;
|
|
Func<T, T, bool> compareFunc;
|
|
|
|
public int Count => count;
|
|
|
|
public SimpleHeap() { }
|
|
|
|
public SimpleHeap(Func<T, T, bool> compareFunc)
|
|
{
|
|
this.compareFunc = compareFunc;
|
|
}
|
|
|
|
public SimpleHeap(IEnumerable<T> collection, Func<T, T, bool> compareFunc)
|
|
{
|
|
this.compareFunc = compareFunc;
|
|
foreach (var item in collection)
|
|
{
|
|
Push(item);
|
|
}
|
|
}
|
|
|
|
public void SetCompareFunc(Func<T, T, bool> compareFunc)
|
|
{
|
|
this.compareFunc = compareFunc;
|
|
}
|
|
|
|
public void Push(T value)
|
|
{
|
|
InsertBack(value);
|
|
RecurcieveClimb(count - 1);
|
|
}
|
|
|
|
public T Pop()
|
|
{
|
|
Debug.Assert(count > 0, "Heap is empty");
|
|
|
|
T result = data[ROOT_IDX];
|
|
Swap(0, --count);
|
|
RecurcieveFall(0);
|
|
|
|
return result;
|
|
}
|
|
|
|
public T GetRootValue()
|
|
{
|
|
Debug.Assert(count > 0, "Heap is empty");
|
|
|
|
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 RecurcieveClimb(int curIdx)
|
|
{
|
|
if (curIdx == ROOT_IDX) return;
|
|
|
|
int parentIdx = (curIdx - 1) >> 1;
|
|
|
|
if (compareFunc(data[curIdx], data[parentIdx]))
|
|
{
|
|
Swap(curIdx, parentIdx);
|
|
RecurcieveClimb(parentIdx);
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
void RecurcieveFall(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);
|
|
RecurcieveFall(aptIdx);
|
|
}
|
|
|
|
void InsertBack(T value)
|
|
{
|
|
if (count >= data.Length)
|
|
Array.Resize(ref data, count + 1);
|
|
|
|
data[count++] = value;
|
|
}
|
|
|
|
int GetLeftChildIdx(int curIdx)
|
|
{
|
|
int result = (curIdx << 1) + 1;
|
|
|
|
if (result >= count)
|
|
result = INVALID_IDX;
|
|
|
|
return result;
|
|
}
|
|
|
|
int GetRightChildIdx(int curIdx)
|
|
{
|
|
int result = (curIdx << 1) + 2;
|
|
|
|
if (result >= count)
|
|
result = INVALID_IDX;
|
|
|
|
return result;
|
|
}
|
|
|
|
int GetAptIndex(int value1Index, int value2Index) => compareFunc(data[value1Index], data[value2Index]) ? value1Index : value2Index;
|
|
|
|
void Swap(int sourceIdx, int destinationIdx)
|
|
{
|
|
T tmp = data[sourceIdx];
|
|
data[sourceIdx] = data[destinationIdx];
|
|
data[destinationIdx] = tmp;
|
|
}
|
|
}
|