using System; using System.Collections.Generic; using System.Collections; namespace Nuclex.Support.Collections { /// Queue that dequeues items in order of their priority public class PriorityQueue : ICollection, IEnumerable { #region class Enumerator /// Enumerates all items contained in a priority queue private class Enumerator : IEnumerator { /// Initializes a new priority queue enumerator /// Priority queue to be enumerated public Enumerator(PriorityQueue priorityQueue) { this.priorityQueue = priorityQueue; Reset(); } /// Resets the enumerator to its initial state public void Reset() { index = -1; version = priorityQueue.version; } /// The current item being enumerated ItemType IEnumerator.Current { get { checkVersion(); return priorityQueue.heap[index]; } } /// Moves to the next item in the priority queue /// True if a next item was found, false if the end has been reached public bool MoveNext() { checkVersion(); if(index + 1 == priorityQueue.count) return false; ++index; return true; } /// Releases all resources used by the enumerator public void Dispose() { } /// Ensures that the priority queue has not changed private void checkVersion() { if(version != priorityQueue.version) throw new InvalidOperationException("Priority queue has been modified"); } /// The current item being enumerated object IEnumerator.Current { get { checkVersion(); return priorityQueue.heap[index]; } } /// Index of the current item in the priority queue private int index; /// The priority queue whose items this instance enumerates private PriorityQueue priorityQueue; /// Expected version of the priority queue private int version; } #endregion // class Enumerator /// Initializes a new priority queue /// Comparer to use for ordering the items public PriorityQueue(IComparer comparer) { this.comparer = comparer; capacity = 15; // 15 is equal to 4 complete levels heap = new ItemType[capacity]; } /// Takes the item with the highest priority off from the queue /// The item with the highest priority in the list public ItemType Dequeue() { if(count == 0) throw new InvalidOperationException("No items available to dequeue"); ItemType result = heap[0]; --count; trickleDown(0, heap[count]); ++version; return result; } /// Puts an item into the priority queue /// Item to be queued public void Enqueue(ItemType item) { if(count == capacity) growHeap(); ++count; bubbleUp(count - 1, item); ++version; } /// Removes all items from the priority queue public void Clear() { this.count = 0; ++version; } /// Total number of items in the priority queue public int Count { get { return this.count; } } /// Copies the contents of the priority queue into an array /// Array to copy the priority queue into /// Starting index for the destination array public void CopyTo(Array array, int index) { Array.Copy(heap, 0, array, index, count); } /// /// Obtains an object that can be used to synchronize accesses to the priority queue /// from different threads /// public object SyncRoot { get { return this; } } /// Whether operations performed on this priority queue are thread safe public bool IsSynchronized { get { return false; } } /// Returns a typesafe enumerator for the priority queue /// A new enumerator for the priority queue public IEnumerator GetEnumerator() { return new Enumerator(this); } /// Moves an item upwards in the heap tree /// Index of the item to be moved /// Item to be moved private void bubbleUp(int index, ItemType item) { int parent = getParent(index); // Note: (index > 0) means there is a parent while((index > 0) && (this.comparer.Compare(heap[parent], item) < 0)) { heap[index] = heap[parent]; index = parent; parent = getParent(index); } heap[index] = item; } /// Moved the item downwards in the heap tree /// Index of the item to be moved /// Item to be moved private void trickleDown(int index, ItemType item) { int child = getLeftChild(index); while(child < count) { if(((child + 1) < count) && (this.comparer.Compare(heap[child], heap[child + 1]) < 0)) ++child; heap[index] = heap[child]; index = child; child = getLeftChild(index); } bubbleUp(index, item); } /// Obtains the left child item in the heap tree /// Index of the item whose left child to return /// The left child item of the provided parent item private int getLeftChild(int index) { return (index * 2) + 1; } /// Calculates the parent entry of the item on the heap /// Index of the item whose parent to calculate /// The index of the parent to the specified item private int getParent(int index) { return (index - 1) / 2; } /// Increases the size of the priority collection's heap private void growHeap() { capacity = (capacity * 2) + 1; ItemType[] newHeap = new ItemType[capacity]; Array.Copy(heap, 0, newHeap, 0, count); heap = newHeap; } /// Returns an enumerator for the priority queue /// A new enumerator for the priority queue IEnumerator IEnumerable.GetEnumerator() { return new Enumerator(this); } /// Comparer used to order the items in the priority queue private IComparer comparer; /// Total number of items in the priority queue private int count; /// Available space in the priority queue private int capacity; /// Incremented whenever the priority queue is modified private int version; /// Tree containing the items in the priority queue private ItemType[] heap; } } // namespace Nuclex.Support.Collections