forked from mirror/DotRecast
added RcBinaryMinHeapTest
This commit is contained in:
parent
8a655528c3
commit
89214accfb
|
@ -1,161 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
|
||||
namespace DotRecast.Core.Collections
|
||||
{
|
||||
public sealed class RcBinaryHeap<T>
|
||||
{
|
||||
public int Count => _count;
|
||||
public int Capacity => _values.Length;
|
||||
|
||||
public T this[int index] => _values[index];
|
||||
|
||||
private T[] _values;
|
||||
private int _count;
|
||||
|
||||
private Comparison<T> _comparision;
|
||||
|
||||
public RcBinaryHeap(Comparison<T> comparison) : this(8, comparison)
|
||||
{
|
||||
}
|
||||
|
||||
public RcBinaryHeap(int capacity, Comparison<T> comparison)
|
||||
{
|
||||
if (capacity <= 0)
|
||||
throw new ArgumentException("capacity must greater than zero");
|
||||
|
||||
_values = new T[capacity];
|
||||
_comparision = comparison;
|
||||
_count = 0;
|
||||
}
|
||||
|
||||
public void Push(T val)
|
||||
{
|
||||
EnsureCapacity();
|
||||
|
||||
_values[_count++] = val;
|
||||
|
||||
UpHeap(_count - 1);
|
||||
}
|
||||
|
||||
public T Pop()
|
||||
{
|
||||
if (_count == 0)
|
||||
{
|
||||
Throw();
|
||||
|
||||
static void Throw() =>
|
||||
throw new InvalidOperationException("no element to pop");
|
||||
}
|
||||
|
||||
Swap(0, --_count);
|
||||
DownHeap(1);
|
||||
|
||||
return _values[_count];
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public T Top()
|
||||
{
|
||||
return _values[0];
|
||||
}
|
||||
|
||||
public void Modify(T node)
|
||||
{
|
||||
for (int i = 0; i < _count; i++)
|
||||
{
|
||||
if (_values[i].Equals(node))
|
||||
{
|
||||
UpHeap(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
Array.Clear(_values, 0, _count);
|
||||
_count = 0;
|
||||
}
|
||||
|
||||
public void FastClear()
|
||||
{
|
||||
_count = 0;
|
||||
}
|
||||
|
||||
public T[] ToArray()
|
||||
{
|
||||
var copy = new T[_count];
|
||||
Array.Copy(_values, copy, _count);
|
||||
return copy;
|
||||
}
|
||||
|
||||
public void ReBuild()
|
||||
{
|
||||
for (int i = _count / 2; i >= 1; i--)
|
||||
{
|
||||
DownHeap(i);
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureCapacity()
|
||||
{
|
||||
if (_values.Length <= _count)
|
||||
{
|
||||
var newValues = new T[Capacity * 2];
|
||||
Array.Copy(_values, newValues, _count);
|
||||
_values = newValues;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpHeap(int i)
|
||||
{
|
||||
int p = (i - 1) / 2;
|
||||
while (p >= 0)
|
||||
{
|
||||
if (_comparision(_values[p], _values[i]) <= 0)
|
||||
break;
|
||||
|
||||
Swap(p, i);
|
||||
|
||||
i = p;
|
||||
p = (i - 1) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
private void DownHeap(int i)
|
||||
{
|
||||
T d = _values[i - 1];
|
||||
int child;
|
||||
while (i <= _count / 2)
|
||||
{
|
||||
child = i * 2;
|
||||
if (child < _count && _comparision(_values[child - 1], _values[child]) > 0)
|
||||
child++;
|
||||
|
||||
if (_comparision(d, _values[child - 1]) <= 0)
|
||||
break;
|
||||
|
||||
_values[i - 1] = _values[child - 1];
|
||||
i = child;
|
||||
}
|
||||
_values[i - 1] = d;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void Swap(int x, int y)
|
||||
{
|
||||
if (x == y)
|
||||
return;
|
||||
(_values[y], _values[x]) = (_values[x], _values[y]);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool IsEmpty()
|
||||
{
|
||||
return _count == 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,154 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace DotRecast.Core.Collections
|
||||
{
|
||||
public sealed class RcBinaryMinHeap<T>
|
||||
{
|
||||
private readonly List<T> _items;
|
||||
private readonly Comparison<T> _comparision;
|
||||
|
||||
public int Count => _items.Count;
|
||||
public int Capacity => _items.Capacity;
|
||||
|
||||
public RcBinaryMinHeap(Comparison<T> comparision)
|
||||
{
|
||||
_items = new List<T>();
|
||||
_comparision = comparision;
|
||||
}
|
||||
|
||||
public RcBinaryMinHeap(int capacity, Comparison<T> comparison) : this(comparison)
|
||||
{
|
||||
if (capacity <= 0)
|
||||
throw new ArgumentException("capacity must greater than zero");
|
||||
|
||||
_items = new List<T>(capacity);
|
||||
_comparision = comparison;
|
||||
}
|
||||
|
||||
public void Push(T val)
|
||||
{
|
||||
_items.Add(val);
|
||||
SiftUp(_items.Count - 1);
|
||||
}
|
||||
|
||||
public T Pop()
|
||||
{
|
||||
var min = Peek();
|
||||
RemoveMin();
|
||||
return min;
|
||||
}
|
||||
|
||||
private void RemoveMin()
|
||||
{
|
||||
if (_items.Count == 0)
|
||||
{
|
||||
Throw();
|
||||
static void Throw() => throw new InvalidOperationException("no element to pop");
|
||||
}
|
||||
|
||||
int last = _items.Count - 1;
|
||||
Swap(0, last);
|
||||
_items.RemoveAt(last);
|
||||
|
||||
MinHeapify(0, last - 1);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public T Top()
|
||||
{
|
||||
return _items[0];
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public T Peek()
|
||||
{
|
||||
if (IsEmpty())
|
||||
{
|
||||
throw new Exception("Heap is empty.");
|
||||
}
|
||||
|
||||
return _items[0];
|
||||
}
|
||||
|
||||
|
||||
public bool Modify(T node)
|
||||
{
|
||||
for (int i = 0; i < _items.Count; i++)
|
||||
{
|
||||
if (_items[i].Equals(node))
|
||||
{
|
||||
SiftUp(i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Clear()
|
||||
{
|
||||
_items.Clear();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool IsEmpty()
|
||||
{
|
||||
return 0 == _items.Count;
|
||||
}
|
||||
|
||||
private void SiftUp(int nodeIndex)
|
||||
{
|
||||
int parent = (nodeIndex - 1) / 2;
|
||||
while (_comparision.Invoke(_items[nodeIndex], _items[parent]) < 0)
|
||||
{
|
||||
Swap(parent, nodeIndex);
|
||||
nodeIndex = parent;
|
||||
parent = (nodeIndex - 1) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void MinHeapify(int nodeIndex, int lastIndex)
|
||||
{
|
||||
int left = (nodeIndex * 2) + 1;
|
||||
int right = left + 1;
|
||||
int smallest = nodeIndex;
|
||||
|
||||
if (left <= lastIndex && _comparision.Invoke(_items[left], _items[nodeIndex]) < 0)
|
||||
smallest = left;
|
||||
|
||||
if (right <= lastIndex && _comparision.Invoke(_items[right], _items[smallest]) < 0)
|
||||
smallest = right;
|
||||
|
||||
if (smallest == nodeIndex)
|
||||
return;
|
||||
|
||||
Swap(nodeIndex, smallest);
|
||||
MinHeapify(smallest, lastIndex);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void Swap(int x, int y)
|
||||
{
|
||||
if (x == y)
|
||||
return;
|
||||
|
||||
(_items[y], _items[x]) = (_items[x], _items[y]);
|
||||
}
|
||||
|
||||
|
||||
public T[] ToArray()
|
||||
{
|
||||
return _items.ToArray();
|
||||
}
|
||||
|
||||
public List<T> ToList()
|
||||
{
|
||||
return new List<T>(_items);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -52,9 +52,9 @@ public class PriorityQueueBenchmarks
|
|||
{
|
||||
[Params(10, 100, 1000, 10000)] public int Count;
|
||||
|
||||
private RcSortedQueue<Node> _rcQueue;
|
||||
private RcBinaryHeap<Node> _heap;
|
||||
private PriorityQueue<Node, Node> _pqueue;
|
||||
private RcSortedQueue<Node> _sq;
|
||||
private RcBinaryMinHeap<Node> _bmHeap;
|
||||
private PriorityQueue<Node, Node> _pq;
|
||||
|
||||
private float[] _priority;
|
||||
|
||||
|
@ -75,26 +75,24 @@ public class PriorityQueueBenchmarks
|
|||
return x.id.CompareTo(y.id);
|
||||
}
|
||||
|
||||
_rcQueue = new(Comp);
|
||||
_heap = new(Count, Comp);
|
||||
_pqueue = new(Count, Comparer<Node>.Create(Comp));
|
||||
_sq = new(Comp);
|
||||
_bmHeap = new(Count, Comp);
|
||||
_pq = new(Count, Comparer<Node>.Create(Comp));
|
||||
|
||||
_priority = new float[Count];
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
_priority[i] = (float)Random.Shared.NextDouble() * 100f;
|
||||
}
|
||||
|
||||
Console.WriteLine("111");
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void Enqueue_rcQueue()
|
||||
public void Enqueue_RcSortedQueue()
|
||||
{
|
||||
_rcQueue.Clear();
|
||||
_sq.Clear();
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
_rcQueue.Enqueue(new Node
|
||||
_sq.Enqueue(new Node
|
||||
{
|
||||
id = i,
|
||||
total = _priority[i],
|
||||
|
@ -103,12 +101,12 @@ public class PriorityQueueBenchmarks
|
|||
}
|
||||
|
||||
[Benchmark]
|
||||
public void Enqueue_heap()
|
||||
public void Enqueue_RcBinaryMinHeap()
|
||||
{
|
||||
_heap.Clear();
|
||||
_bmHeap.Clear();
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
_heap.Push(new Node
|
||||
_bmHeap.Push(new Node
|
||||
{
|
||||
id = i,
|
||||
total = _priority[i],
|
||||
|
@ -117,9 +115,9 @@ public class PriorityQueueBenchmarks
|
|||
}
|
||||
|
||||
[Benchmark]
|
||||
public void Enqueue_pqueue()
|
||||
public void Enqueue_PriorityQueue()
|
||||
{
|
||||
_pqueue.Clear();
|
||||
_pq.Clear();
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
var node = new Node
|
||||
|
@ -127,52 +125,52 @@ public class PriorityQueueBenchmarks
|
|||
id = i,
|
||||
total = _priority[i],
|
||||
};
|
||||
_pqueue.Enqueue(node, node);
|
||||
_pq.Enqueue(node, node);
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void DequeueAll_rcQueue()
|
||||
public void DequeueAll_RcSortedQueue()
|
||||
{
|
||||
_rcQueue.Clear();
|
||||
_sq.Clear();
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
_rcQueue.Enqueue(new Node
|
||||
_sq.Enqueue(new Node
|
||||
{
|
||||
id = i,
|
||||
total = _priority[i],
|
||||
});
|
||||
}
|
||||
|
||||
while (_rcQueue.Count() > 0)
|
||||
while (_sq.Count() > 0)
|
||||
{
|
||||
_rcQueue.Dequeue();
|
||||
_sq.Dequeue();
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void DequeueAll_heap()
|
||||
public void DequeueAll_RcBinaryMinHeap()
|
||||
{
|
||||
_heap.Clear();
|
||||
_bmHeap.Clear();
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
_heap.Push(new Node
|
||||
_bmHeap.Push(new Node
|
||||
{
|
||||
id = i,
|
||||
total = _priority[i],
|
||||
});
|
||||
}
|
||||
|
||||
while (_heap.Count > 0)
|
||||
while (_bmHeap.Count > 0)
|
||||
{
|
||||
_heap.Pop();
|
||||
_bmHeap.Pop();
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void DequeueAll_pqueue()
|
||||
public void DequeueAll_PriorityQueue()
|
||||
{
|
||||
_pqueue.Clear();
|
||||
_pq.Clear();
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
var node = new Node
|
||||
|
@ -180,52 +178,52 @@ public class PriorityQueueBenchmarks
|
|||
id = i,
|
||||
total = _priority[i],
|
||||
};
|
||||
_pqueue.Enqueue(node, node);
|
||||
_pq.Enqueue(node, node);
|
||||
}
|
||||
|
||||
while (_pqueue.Count > 0)
|
||||
while (_pq.Count > 0)
|
||||
{
|
||||
_pqueue.Dequeue();
|
||||
_pq.Dequeue();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Benchmark]
|
||||
public void EnqueueDequeue_rcQueue()
|
||||
public void EnqueueDequeue_RcSortedQueue()
|
||||
{
|
||||
_rcQueue.Clear();
|
||||
_sq.Clear();
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
_rcQueue.Enqueue(new Node
|
||||
_sq.Enqueue(new Node
|
||||
{
|
||||
id = i,
|
||||
total = _priority[i],
|
||||
});
|
||||
|
||||
_rcQueue.Dequeue();
|
||||
_sq.Dequeue();
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void EnqueueDequeue_heap()
|
||||
public void EnqueueDequeue_RcBinaryMinHeap()
|
||||
{
|
||||
_heap.Clear();
|
||||
_bmHeap.Clear();
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
_heap.Push(new Node
|
||||
_bmHeap.Push(new Node
|
||||
{
|
||||
id = i,
|
||||
total = _priority[i],
|
||||
});
|
||||
|
||||
_heap.Pop();
|
||||
_bmHeap.Pop();
|
||||
}
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void EnqueueDequeue_pqueue()
|
||||
public void EnqueueDequeue_PriorityQueue()
|
||||
{
|
||||
_pqueue.Clear();
|
||||
_pq.Clear();
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
var node = new Node
|
||||
|
@ -233,9 +231,9 @@ public class PriorityQueueBenchmarks
|
|||
id = i,
|
||||
total = _priority[i],
|
||||
};
|
||||
_pqueue.Enqueue(node, node);
|
||||
_pq.Enqueue(node, node);
|
||||
|
||||
_pqueue.Dequeue();
|
||||
_pq.Dequeue();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
using DotRecast.Core.Collections;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace DotRecast.Core.Test;
|
||||
|
||||
public class RcBinaryMinHeapTest
|
||||
{
|
||||
private static readonly RcAtomicLong Gen = new();
|
||||
|
||||
private class Node
|
||||
{
|
||||
public readonly long Id;
|
||||
public long Value;
|
||||
|
||||
public Node(int value)
|
||||
{
|
||||
Id = Gen.IncrementAndGet();
|
||||
Value = value;
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPush()
|
||||
{
|
||||
var minHeap = new RcBinaryMinHeap<Node>((x, y) => x.Value.CompareTo(y.Value));
|
||||
|
||||
minHeap.Push(new Node(5));
|
||||
minHeap.Push(new Node(3));
|
||||
minHeap.Push(new Node(7));
|
||||
minHeap.Push(new Node(2));
|
||||
minHeap.Push(new Node(4));
|
||||
|
||||
// Push 후 힙의 속성을 검증
|
||||
AssertHeapProperty(minHeap.ToArray());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPop()
|
||||
{
|
||||
var minHeap = new RcBinaryMinHeap<Node>((x, y) => x.Value.CompareTo(y.Value));
|
||||
|
||||
minHeap.Push(new Node(5));
|
||||
minHeap.Push(new Node(3));
|
||||
minHeap.Push(new Node(7));
|
||||
minHeap.Push(new Node(2));
|
||||
minHeap.Push(new Node(4));
|
||||
|
||||
// Pop을 통해 최소 값부터 순서대로 제거하면서 검증
|
||||
Assert.That(minHeap.Pop().Value, Is.EqualTo(2));
|
||||
Assert.That(minHeap.Pop().Value, Is.EqualTo(3));
|
||||
Assert.That(minHeap.Pop().Value, Is.EqualTo(4));
|
||||
Assert.That(minHeap.Pop().Value, Is.EqualTo(5));
|
||||
Assert.That(minHeap.Pop().Value, Is.EqualTo(7));
|
||||
|
||||
// 모든 요소를 Pop한 후에는 비어있어야 함
|
||||
Assert.That(minHeap.IsEmpty(), Is.True);
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public void TestTop()
|
||||
{
|
||||
var minHeap = new RcBinaryMinHeap<Node>((x, y) => x.Value.CompareTo(y.Value));
|
||||
|
||||
minHeap.Push(new Node(5));
|
||||
minHeap.Push(new Node(3));
|
||||
minHeap.Push(new Node(7));
|
||||
|
||||
Assert.That(minHeap.Top().Value, Is.EqualTo(3));
|
||||
AssertHeapProperty(minHeap.ToArray());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestModify()
|
||||
{
|
||||
var minHeap = new RcBinaryMinHeap<Node>((x, y) => x.Value.CompareTo(y.Value));
|
||||
|
||||
var node7 = new Node(7);
|
||||
minHeap.Push(new Node(5));
|
||||
minHeap.Push(new Node(3));
|
||||
minHeap.Push(node7);
|
||||
minHeap.Push(new Node(2));
|
||||
minHeap.Push(new Node(4));
|
||||
|
||||
node7.Value = 1;
|
||||
var result = minHeap.Modify(node7); // Modify value 7 to 1
|
||||
var result2 = minHeap.Modify(new Node(4));
|
||||
|
||||
Assert.That(result, Is.EqualTo(true));
|
||||
Assert.That(result2, Is.EqualTo(false));
|
||||
Assert.That(minHeap.Top().Value, Is.EqualTo(1));
|
||||
AssertHeapProperty(minHeap.ToArray());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCount()
|
||||
{
|
||||
var minHeap = new RcBinaryMinHeap<Node>((x, y) => x.Value.CompareTo(y.Value));
|
||||
|
||||
minHeap.Push(new Node(5));
|
||||
minHeap.Push(new Node(3));
|
||||
minHeap.Push(new Node(7));
|
||||
|
||||
Assert.That(minHeap.Count, Is.EqualTo(3));
|
||||
|
||||
minHeap.Pop();
|
||||
|
||||
Assert.That(minHeap.Count, Is.EqualTo(2));
|
||||
|
||||
minHeap.Clear();
|
||||
|
||||
Assert.That(minHeap.Count, Is.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestIsEmpty()
|
||||
{
|
||||
var minHeap = new RcBinaryMinHeap<Node>((x, y) => x.Value.CompareTo(y.Value));
|
||||
|
||||
Assert.That(minHeap.IsEmpty(), Is.True);
|
||||
|
||||
minHeap.Push(new Node(5));
|
||||
|
||||
Assert.That(minHeap.IsEmpty(), Is.False);
|
||||
|
||||
minHeap.Pop();
|
||||
|
||||
Assert.That(minHeap.IsEmpty(), Is.True);
|
||||
}
|
||||
|
||||
private void AssertHeapProperty(Node[] array)
|
||||
{
|
||||
for (int i = 0; i < array.Length / 2; i++)
|
||||
{
|
||||
int leftChildIndex = 2 * i + 1;
|
||||
int rightChildIndex = 2 * i + 2;
|
||||
|
||||
// 왼쪽 자식 노드가 있는지 확인하고 비교
|
||||
if (leftChildIndex < array.Length)
|
||||
Assert.That(array[i].Value, Is.LessThanOrEqualTo(array[leftChildIndex].Value));
|
||||
|
||||
// 오른쪽 자식 노드가 있는지 확인하고 비교
|
||||
if (rightChildIndex < array.Length)
|
||||
Assert.That(array[i].Value, Is.LessThanOrEqualTo(array[rightChildIndex].Value));
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue