fdf4442163
git-svn-id: file:///srv/devel/repo-conversion/nusu@329 d2e56fa2-650e-0410-a79f-9358c0239efd
286 lines
9.2 KiB
C#
286 lines
9.2 KiB
C#
#region CPL License
|
|
/*
|
|
Nuclex Framework
|
|
Copyright (C) 2002-2017 Nuclex Development Labs
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the IBM Common Public License as
|
|
published by the IBM Corporation; either version 1.0 of the
|
|
License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
IBM Common Public License for more details.
|
|
|
|
You should have received a copy of the IBM Common Public
|
|
License along with this library
|
|
*/
|
|
#endregion
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections;
|
|
|
|
namespace Nuclex.Support.Collections {
|
|
|
|
/// <summary>Queue that dequeues items in order of their priority</summary>
|
|
public class PriorityQueue<TItem> : ICollection, IEnumerable<TItem> {
|
|
|
|
#region class Enumerator
|
|
|
|
/// <summary>Enumerates all items contained in a priority queue</summary>
|
|
private class Enumerator : IEnumerator<TItem> {
|
|
|
|
/// <summary>Initializes a new priority queue enumerator</summary>
|
|
/// <param name="priorityQueue">Priority queue to be enumerated</param>
|
|
public Enumerator(PriorityQueue<TItem> priorityQueue) {
|
|
this.priorityQueue = priorityQueue;
|
|
Reset();
|
|
}
|
|
|
|
/// <summary>Resets the enumerator to its initial state</summary>
|
|
public void Reset() {
|
|
this.index = -1;
|
|
#if DEBUG
|
|
this.expectedVersion = this.priorityQueue.version;
|
|
#endif
|
|
}
|
|
|
|
/// <summary>The current item being enumerated</summary>
|
|
TItem IEnumerator<TItem>.Current {
|
|
get {
|
|
#if DEBUG
|
|
checkVersion();
|
|
#endif
|
|
return this.priorityQueue.heap[index];
|
|
}
|
|
}
|
|
|
|
/// <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() {
|
|
#if DEBUG
|
|
checkVersion();
|
|
#endif
|
|
if(this.index + 1 == this.priorityQueue.count)
|
|
return false;
|
|
|
|
++this.index;
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>Releases all resources used by the enumerator</summary>
|
|
public void Dispose() { }
|
|
|
|
#if DEBUG
|
|
/// <summary>Ensures that the priority queue has not changed</summary>
|
|
private void checkVersion() {
|
|
if(this.expectedVersion != this.priorityQueue.version)
|
|
throw new InvalidOperationException("Priority queue has been modified");
|
|
}
|
|
#endif
|
|
|
|
/// <summary>The current item being enumerated</summary>
|
|
object IEnumerator.Current {
|
|
get {
|
|
#if DEBUG
|
|
checkVersion();
|
|
#endif
|
|
return this.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<TItem> priorityQueue;
|
|
#if DEBUG
|
|
/// <summary>Expected version of the priority queue</summary>
|
|
private int expectedVersion;
|
|
#endif
|
|
|
|
}
|
|
|
|
#endregion // class Enumerator
|
|
|
|
/// <summary>
|
|
/// Initializes a new priority queue using IComparable for comparing items
|
|
/// </summary>
|
|
public PriorityQueue() : this(Comparer<TItem>.Default) { }
|
|
|
|
/// <summary>Initializes a new priority queue</summary>
|
|
/// <param name="comparer">Comparer to use for ordering the items</param>
|
|
public PriorityQueue(IComparer<TItem> comparer) {
|
|
this.comparer = comparer;
|
|
this.capacity = 15; // 15 is equal to 4 complete levels
|
|
this.heap = new TItem[this.capacity];
|
|
}
|
|
|
|
/// <summary>Returns the topmost item in the queue without dequeueing it</summary>
|
|
/// <returns>The topmost item in the queue</returns>
|
|
public TItem Peek() {
|
|
if(this.count == 0) {
|
|
throw new InvalidOperationException("No items queued");
|
|
}
|
|
|
|
return this.heap[0];
|
|
}
|
|
|
|
/// <summary>Takes the item with the highest priority off from the queue</summary>
|
|
/// <returns>The item with the highest priority in the list</returns>
|
|
/// <exception cref="InvalidOperationException">When the queue is empty</exception>
|
|
public TItem Dequeue() {
|
|
if(this.count == 0) {
|
|
throw new InvalidOperationException("No items available to dequeue");
|
|
}
|
|
|
|
TItem result = this.heap[0];
|
|
--this.count;
|
|
trickleDown(0, this.heap[this.count]);
|
|
#if DEBUG
|
|
++this.version;
|
|
#endif
|
|
return result;
|
|
}
|
|
|
|
/// <summary>Puts an item into the priority queue</summary>
|
|
/// <param name="item">Item to be queued</param>
|
|
public void Enqueue(TItem item) {
|
|
if(this.count == capacity)
|
|
growHeap();
|
|
|
|
++this.count;
|
|
bubbleUp(this.count - 1, item);
|
|
#if DEBUG
|
|
++this.version;
|
|
#endif
|
|
}
|
|
|
|
/// <summary>Removes all items from the priority queue</summary>
|
|
public void Clear() {
|
|
this.count = 0;
|
|
#if DEBUG
|
|
++this.version;
|
|
#endif
|
|
}
|
|
|
|
|
|
/// <summary>Total number of items in the priority queue</summary>
|
|
public int Count {
|
|
get { return this.count; }
|
|
}
|
|
|
|
/// <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>
|
|
public void CopyTo(Array array, int index) {
|
|
Array.Copy(this.heap, 0, array, index, this.count);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Obtains an object that can be used to synchronize accesses to the priority queue
|
|
/// from different threads
|
|
/// </summary>
|
|
public object SyncRoot {
|
|
get { return this; }
|
|
}
|
|
|
|
/// <summary>Whether operations performed on this priority queue are thread safe</summary>
|
|
public bool IsSynchronized {
|
|
get { return false; }
|
|
}
|
|
|
|
/// <summary>Returns a typesafe enumerator for the priority queue</summary>
|
|
/// <returns>A new enumerator for the priority queue</returns>
|
|
public IEnumerator<TItem> GetEnumerator() {
|
|
return new Enumerator(this);
|
|
}
|
|
|
|
/// <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, TItem item) {
|
|
int parent = getParent(index);
|
|
|
|
// Note: (index > 0) means there is a parent
|
|
while((index > 0) && (this.comparer.Compare(this.heap[parent], item) < 0)) {
|
|
this.heap[index] = this.heap[parent];
|
|
index = parent;
|
|
parent = getParent(index);
|
|
}
|
|
|
|
this.heap[index] = item;
|
|
}
|
|
|
|
/// <summary>Move the item downwards 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 trickleDown(int index, TItem item) {
|
|
int child = getLeftChild(index);
|
|
|
|
while(child < this.count) {
|
|
|
|
bool needsToBeMoved =
|
|
((child + 1) < this.count) &&
|
|
(this.comparer.Compare(heap[child], this.heap[child + 1]) < 0);
|
|
|
|
if(needsToBeMoved)
|
|
++child;
|
|
|
|
this.heap[index] = this.heap[child];
|
|
index = child;
|
|
child = getLeftChild(index);
|
|
|
|
}
|
|
|
|
bubbleUp(index, item);
|
|
}
|
|
|
|
/// <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;
|
|
}
|
|
|
|
/// <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;
|
|
}
|
|
|
|
/// <summary>Increases the size of the priority collection's heap</summary>
|
|
private void growHeap() {
|
|
this.capacity = (capacity * 2) + 1;
|
|
|
|
TItem[] newHeap = new TItem[this.capacity];
|
|
Array.Copy(this.heap, 0, newHeap, 0, this.count);
|
|
this.heap = newHeap;
|
|
}
|
|
|
|
/// <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);
|
|
}
|
|
|
|
/// <summary>Comparer used to order the items in the priority queue</summary>
|
|
private IComparer<TItem> 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>Tree containing the items in the priority queue</summary>
|
|
private TItem[] heap;
|
|
#if DEBUG
|
|
/// <summary>Incremented whenever the priority queue is modified</summary>
|
|
private int version;
|
|
#endif
|
|
|
|
}
|
|
|
|
} // namespace Nuclex.Support.Collections
|