// 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; } } } }