using System; using System.Collections.Generic; using System.Text; using System.Collections; using System.Runtime.InteropServices; namespace Brains.Framework { /// /// Represents an item stored in a priority queue. /// /// The type of object in the queue. /// The type of the priority field. [Serializable] internal struct PriorityQueueItem { private TValue value; private TPriority priority; public PriorityQueueItem(TValue val, TPriority pri) { this.value = val; this.priority = pri; } /// /// Gets the value of this PriorityQueueItem. /// public TValue Value { get { return value; } } /// /// Gets the priority associated with this PriorityQueueItem. /// public TPriority Priority { get { return priority; } } } /// /// Represents a binary heap priority queue. /// /// The type of object in the queue. /// The type of the priority field. [Serializable] [ComVisible(false)] internal class PriorityQueue : ICollection, IEnumerable> { private PriorityQueueItem[] items; private const Int32 DefaultCapacity = 16; private Int32 capacity; private Int32 numItems; private Comparison compareFunc; /// /// Initializes a new instance of the PriorityQueue class that is empty, /// has the default initial capacity, and uses the default IComparer. /// public PriorityQueue() : this(DefaultCapacity, Comparer.Default) { } /// /// Initializes a new instance of the PriorityQueue class that is empty, /// has the specified initial capacity, and uses the default IComparer. /// /// Desired initial capacity. public PriorityQueue(Int32 initialCapacity) : this(initialCapacity, Comparer.Default) { } /// /// Initializes a new instance of the PriorityQueue class that is empty, /// has the default initial capacity, and uses the specified IComparer. /// /// An object that implements the IComparer interface /// for items of type TPriority. public PriorityQueue(IComparer comparer) : this(DefaultCapacity, comparer) { } /// /// Initializes a new instance of the PriorityQueue class that is empty, /// has the specified initial capacity, and uses the specified IComparer. /// /// Desired initial capacity. /// An object that implements the IComparer interface /// for items of type TPriority. public PriorityQueue(int initialCapacity, IComparer comparer) { Init(initialCapacity, new Comparison(comparer.Compare)); } /// /// Initializes a new instance of the PriorityQueue class that is empty, /// has the default initial capacity, and uses the specified comparison /// function for comparing items of type TPriority. /// /// The comparison function. public PriorityQueue(Comparison comparison) : this(DefaultCapacity, comparison) { } /// /// Initializes a new instance of the PriorityQueue class that is empty, /// has the specified initial capacity, and uses the specified comparison /// function for comparing items of type TPriority. /// /// The desired initial capacity. /// The comparison function. public PriorityQueue(int initialCapacity, Comparison comparison) { Init(initialCapacity, comparison); } // Initializes the queue private void Init(int initialCapacity, Comparison comparison) { numItems = 0; compareFunc = comparison; SetCapacity(initialCapacity); } /// /// Gets the number of items that are currently in the queue. /// public int Count { get { return numItems; } } /// /// Gets or sets the queue's capacity. /// public int Capacity { get { return items.Length; } set { SetCapacity(value); } } // Set the queue's capacity. private void SetCapacity(int newCapacity) { int newCap = newCapacity; if (newCap < DefaultCapacity) newCap = DefaultCapacity; // throw exception if newCapacity < numItems if (newCap < numItems) throw new ArgumentOutOfRangeException("newCapacity", "New capacity is less than Count"); this.capacity = newCap; if (items == null) { // Initial allocation. items = new PriorityQueueItem[newCap]; return; } // Resize the array. Array.Resize(ref items, newCap); } /// /// Adds an object to the queue, in order by priority. /// /// The object to be added. /// Priority of the object to be added. public void Enqueue(TValue value, TPriority priority) { if (numItems == capacity) { // need to increase capacity // grow by 50 percent //SetCapacity((3 * Capacity) / 2); SetCapacity((int)(Capacity * 1.5)); } // Create the new item PriorityQueueItem newItem = new PriorityQueueItem(value, priority); int i = numItems; ++numItems; // and insert it into the heap. while ((i > 0) && (compareFunc(items[i / 2].Priority, newItem.Priority) > 0)) { items[i] = items[i / 2]; i /= 2; } items[i] = newItem; } // Remove a node at a particular position in the queue. private PriorityQueueItem RemoveAt(Int32 index) { // remove an item from the heap PriorityQueueItem o = items[index]; PriorityQueueItem tmp = items[numItems - 1]; items[--numItems] = default(PriorityQueueItem); if (numItems > 0) { int i = index; int j = i + 1; while (i < Count / 2) { if ((j < Count - 1) && (compareFunc(items[j].Priority, items[j + 1].Priority) > 0)) { j++; } if (compareFunc(items[j].Priority, tmp.Priority) >= 0) { break; } items[i] = items[j]; i = j; j *= 2; } items[i] = tmp; } return o; } /// /// Removes and returns the item with the highest priority from the queue. /// /// The object that is removed from the beginning of the queue. public PriorityQueueItem Dequeue() { if (Count == 0) throw new InvalidOperationException("The queue is empty"); return RemoveAt(0); } /// /// Removes the item with the specified value from the queue. /// The passed equality comparison is used. /// /// The item to be removed. /// An object that implements the IEqualityComparer interface /// for the type of item in the collection. public void Remove(TValue item, IEqualityComparer comparer) { // need to find the PriorityQueueItem that has the Data value of o for (int index = 0; index < numItems; ++index) { if (comparer.Equals(item, items[index].Value)) { RemoveAt(index); return; } } throw new ApplicationException("The specified itemm is not in the queue."); } /// /// Removes the item with the specified value from the queue. /// The default type comparison function is used. /// /// The item to be removed. public void Remove(TValue item) { Remove(item, EqualityComparer.Default); } /// /// Returns the object at the beginning of the priority queue without removing it. /// /// The object at the beginning of the queue. public PriorityQueueItem Peek() { if (Count == 0) throw new InvalidOperationException("The queue is empty"); return items[0]; } /// /// Removes all objects from the queue. /// public void Clear() { numItems = 0; TrimExcess(); } /// /// Sets the capacity to the actual number of elements in the Queue, /// if that number is less than 90 percent of current capacity. /// public void TrimExcess() { if (numItems < (0.9 * capacity)) SetCapacity(numItems); } /// /// Determines whether an element is in the queue. /// /// The object to locate in the queue. /// True if item found in the queue. False otherwise. public bool Contains(TValue item) { foreach (PriorityQueueItem qItem in items) { if (qItem.Value.Equals(item)) return true; } return false; } /// /// Copies the elements of the ICollection to an Array, starting at a particular Array index. /// /// The one-dimensional Array that is the destination of the elements copied from ICollection. /// The Array must have zero-based indexing. /// The zero-based index in array at which copying begins. public void CopyTo(PriorityQueueItem[] array, int arrayIndex) { if (array == null) throw new ArgumentNullException("array"); if (arrayIndex < 0) throw new ArgumentOutOfRangeException("arrayIndex", "arrayIndex is less than 0."); if (array.Rank > 1) throw new ArgumentException("array is multidimensional."); if (arrayIndex >= array.Length) throw new ArgumentException("arrayIndex is equal to or greater than the length of the array."); if (numItems > (array.Length - arrayIndex)) throw new ArgumentException("The number of elements in the source ICollection is greater than the available space from arrayIndex to the end of the destination array."); Array.Copy(items, 0, array, arrayIndex, numItems); } /// /// Copies the queue elements to a new array. /// /// A new array containing elements copied from the Queue. public PriorityQueueItem[] ToArray() { PriorityQueueItem[] newItems = new PriorityQueueItem[numItems]; Array.Copy(items, newItems, numItems); return newItems; } #region ICollection Members /// /// Copies the elements of the ICollection to an Array, starting at a particular Array index. /// /// The one-dimensional Array that is the destination of the elements copied from ICollection. /// The Array must have zero-based indexing. /// The zero-based index in array at which copying begins. void ICollection.CopyTo(Array array, int index) { this.CopyTo((PriorityQueueItem[])array, index); } /// /// Gets a value indicating whether access to the ICollection is synchronized (thread safe). /// bool ICollection.IsSynchronized { get { return false; } } /// /// Gets an object that can be used to synchronize access to the ICollection. /// object ICollection.SyncRoot { get { return items.SyncRoot; } } #endregion #region IEnumerable> Members /// /// Returns an enumerator that iterates through a collection. /// /// An IEnumerator that can be used to iterate through the collection. public IEnumerator> GetEnumerator() { for (int i = 0; i < numItems; i++) { yield return items[i]; } } #endregion #region IEnumerable Members /// /// Returns an enumerator that iterates through a collection. /// /// An IEnumerator that can be used to iterate through the collection. IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } #endregion } #region Interfaces public interface IPriorityQueue { #region Methods int Push(T item); T Pop(); T Peek(); void Update(int i); #endregion } #endregion internal class PriorityQueueB : IPriorityQueue { #region Variables Declaration protected List InnerList = new List(); protected IComparer mComparer; #endregion #region Contructors public PriorityQueueB() { mComparer = Comparer.Default; } public PriorityQueueB(IComparer comparer) { mComparer = comparer; } public PriorityQueueB(IComparer comparer, int capacity) { mComparer = comparer; InnerList.Capacity = capacity; } #endregion #region Methods protected void SwitchElements(int i, int j) { T h = InnerList[i]; InnerList[i] = InnerList[j]; InnerList[j] = h; } protected virtual int OnCompare(int i, int j) { return mComparer.Compare(InnerList[i], InnerList[j]); } /// /// Push an object onto the PQ /// /// The new object /// The index in the list where the object is _now_. This will change when objects are taken from or put onto the PQ. public int Push(T item) { int p = InnerList.Count, p2; InnerList.Add(item); // E[p] = O do { if (p == 0) break; p2 = (p - 1) / 2; if (OnCompare(p, p2) < 0) { SwitchElements(p, p2); p = p2; } else break; } while (true); return p; } /// /// Get the smallest object and remove it. /// /// The smallest object public T Pop() { T result = InnerList[0]; int p = 0, p1, p2, pn; InnerList[0] = InnerList[InnerList.Count - 1]; InnerList.RemoveAt(InnerList.Count - 1); do { pn = p; p1 = 2 * p + 1; p2 = 2 * p + 2; if (InnerList.Count > p1 && OnCompare(p, p1) > 0) // links kleiner p = p1; if (InnerList.Count > p2 && OnCompare(p, p2) > 0) // rechts noch kleiner p = p2; if (p == pn) break; SwitchElements(p, pn); } while (true); return result; } /// /// Notify the PQ that the object at position i has changed /// and the PQ needs to restore order. /// Since you dont have access to any indexes (except by using the /// explicit IList.this) you should not call this function without knowing exactly /// what you do. /// /// The index of the changed object. public void Update(int i) { int p = i, pn; int p1, p2; do // aufsteigen { if (p == 0) break; p2 = (p - 1) / 2; if (OnCompare(p, p2) < 0) { SwitchElements(p, p2); p = p2; } else break; } while (true); if (p < i) return; do // absteigen { pn = p; p1 = 2 * p + 1; p2 = 2 * p + 2; if (InnerList.Count > p1 && OnCompare(p, p1) > 0) // links kleiner p = p1; if (InnerList.Count > p2 && OnCompare(p, p2) > 0) // rechts noch kleiner p = p2; if (p == pn) break; SwitchElements(p, pn); } while (true); } /// /// Get the smallest object without removing it. /// /// The smallest object public T Peek() { if (InnerList.Count > 0) return InnerList[0]; return default(T); } public void Clear() { InnerList.Clear(); } public int Count { get { return InnerList.Count; } } public void RemoveLocation(T item) { int index = -1; for (int i = 0; i < InnerList.Count; i++) { if (mComparer.Compare(InnerList[i], item) == 0) index = i; } if (index != -1) InnerList.RemoveAt(index); } public T this[int index] { get { return InnerList[index]; } set { InnerList[index] = value; Update(index); } } #endregion } }