// This file is provided under The MIT License as part of RiptideNetworking.
// Copyright (c) Tom Weiland
// For additional information please see the included LICENSE.md file or view it on GitHub:
// https://github.com/RiptideNetworking/Riptide/blob/main/LICENSE.md
using System;
using System.Collections.Generic;
namespace Riptide.Utils
{
// PriorityQueue unfortunately doesn't exist in .NET Standard 2.1
/// Represents a collection of items that have a value and a priority. On dequeue, the item with the lowest priority value is removed.
/// Specifies the type of elements in the queue.
/// Specifies the type of priority associated with enqueued elements.
public class PriorityQueue
{
/// Gets the number of elements contained in the .
public int Count { get; private set; }
private const int DefaultCapacity = 8;
private Entry[] heap;
private readonly IComparer comparer;
/// Initializes a new instance of the class.
/// Initial capacity to allocate for the underlying heap array.
public PriorityQueue(int capacity = DefaultCapacity)
{
heap = new Entry[capacity];
comparer = Comparer.Default;
}
/// Initializes a new instance of the class with the specified custom priority comparer.
/// Custom comparer dictating the ordering of elements.
/// Initial capacity to allocate for the underlying heap array.
public PriorityQueue(IComparer comparer, int capacity = DefaultCapacity)
{
heap = new Entry[capacity];
this.comparer = comparer;
}
/// Adds the specified element and associated priority to the .
/// The element to add.
/// The priority with which to associate the new element.
public void Enqueue(TElement element, TPriority priority)
{
if (Count == heap.Length)
{
// Resizing is necessary
Entry[] temp = new Entry[Count * 2];
Array.Copy(heap, temp, heap.Length);
heap = temp;
}
int index = Count;
while (index > 0)
{
int parentIndex = GetParentIndex(index);
if (comparer.Compare(priority, heap[parentIndex].Priority) < 0)
{
heap[index] = heap[parentIndex];
index = parentIndex;
}
else
break;
}
heap[index] = new Entry(element, priority);
Count++;
}
/// Removes and returns the lowest priority element.
public TElement Dequeue()
{
TElement returnValue = heap[0].Element;
if (Count > 1)
{
int parent = 0;
int leftChild = GetLeftChildIndex(parent);
while (leftChild < Count)
{
int rightChild = leftChild + 1;
int bestChild = (rightChild < Count && comparer.Compare(heap[rightChild].Priority, heap[leftChild].Priority) < 0) ? rightChild : leftChild;
heap[parent] = heap[bestChild];
parent = bestChild;
leftChild = GetLeftChildIndex(parent);
}
heap[parent] = heap[Count - 1];
}
Count--;
return returnValue;
}
/// Removes the lowest priority element from the and copies it and its associated priority to the and arguments.
/// When this method returns, contains the removed element.
/// When this method returns, contains the priority associated with the removed element.
/// true if the element is successfully removed; false if the is empty.
public bool TryDequeue(out TElement element, out TPriority priority)
{
if (Count > 0)
{
priority = heap[0].Priority;
element = Dequeue();
return true;
}
{
element = default(TElement);
priority = default(TPriority);
return false;
}
}
/// Returns the lowest priority element.
public TElement Peek()
{
return heap[0].Element;
}
/// Returns the priority of the lowest priority element.
public TPriority PeekPriority()
{
return heap[0].Priority;
}
/// Removes all elements from the .
public void Clear()
{
Array.Clear(heap, 0, heap.Length);
Count = 0;
}
private static int GetParentIndex(int index)
{
return (index - 1) / 2;
}
private static int GetLeftChildIndex(int index)
{
return (index * 2) + 1;
}
private struct Entry
{
internal readonly TEle Element;
internal readonly TPrio Priority;
public Entry(TEle element, TPrio priority)
{
Element = element;
Priority = priority;
}
}
}
}