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