Britbot
PriorityQueue/HeapPriorityQueue.cs
Go to the documentation of this file.
00001 #region Usings
00002 
00003 using System;
00004 using System.Collections;
00005 using System.Collections.Generic;
00006 
00007 #endregion
00008 
00009 namespace Britbot.PriorityQueue
00010 {
00016     public sealed class HeapPriorityQueue<T> : IPriorityQueue<T>
00017         where T : PriorityQueueNode
00018     {
00019         private T[] _nodes;
00020         private long _numNodesEverEnqueued;
00021 
00026         public HeapPriorityQueue(int maxNodes)
00027         {
00028             this.Count = 0;
00029             this._nodes = new T[maxNodes + 1];
00030             this._numNodesEverEnqueued = 0;
00031         }
00032 
00037         public void Set(HeapPriorityQueue<T> queue)
00038         {
00039             this.Count = queue.Count;
00040             //this._nodes = new T[queue._nodes.Length];
00041             this._numNodesEverEnqueued = queue._numNodesEverEnqueued;
00042             //copy array
00043             for(int i = 0; i < queue._numNodesEverEnqueued;i++)
00044             {
00045                 this._nodes[i] = queue._nodes[i];
00046             }
00047         }
00051         public int Count { get; private set; }
00052 
00058         public int MaxSize
00059         {
00060             get { return this._nodes.Length - 1; }
00061         }
00062 
00066 #if NET_VERSION_4_5
00067         [MethodImpl(MethodImplOptions.AggressiveInlining)]
00068         #endif
00069         public void Clear()
00070         {
00071             Array.Clear(this._nodes, 1, this.Count);
00072             this.Count = 0;
00073         }
00074 
00078 #if NET_VERSION_4_5
00079         [MethodImpl(MethodImplOptions.AggressiveInlining)]
00080         #endif
00081         public bool Contains(T node)
00082         {
00083             return (this._nodes[node.QueueIndex] == node);
00084         }
00085 
00089 #if NET_VERSION_4_5
00090         [MethodImpl(MethodImplOptions.AggressiveInlining)]
00091         #endif
00092         public void Enqueue(T node, double priority)
00093         {
00094             node.Priority = priority;
00095             this.Count++;
00096             this._nodes[this.Count] = node;
00097             node.QueueIndex = this.Count;
00098             node.InsertionIndex = this._numNodesEverEnqueued++;
00099             this.CascadeUp(this._nodes[this.Count]);
00100         }
00101 
00102 #if NET_VERSION_4_5
00103         [MethodImpl(MethodImplOptions.AggressiveInlining)]
00104         #endif
00105 
00106         private void Swap(T node1, T node2)
00107         {
00108             //Swap the nodes
00109             this._nodes[node1.QueueIndex] = node2;
00110             this._nodes[node2.QueueIndex] = node1;
00111 
00112             //Swap their indicies
00113             int temp = node1.QueueIndex;
00114             node1.QueueIndex = node2.QueueIndex;
00115             node2.QueueIndex = temp;
00116         }
00117 
00118         //Performance appears to be slightly better when this is NOT inlined o_O
00119         private void CascadeUp(T node)
00120         {
00121             //aka Heapify-up
00122             int parent = node.QueueIndex / 2;
00123             while (parent >= 1)
00124             {
00125                 T parentNode = this._nodes[parent];
00126                 if (this.HasHigherPriority(parentNode, node))
00127                     break;
00128 
00129                 //Node has lower priority value, so move it up the heap
00130                 this.Swap(node, parentNode);
00131                 //For some reason, this is faster with Swap() rather than (less..?) individual operations, like in CascadeDown()
00132 
00133                 parent = node.QueueIndex / 2;
00134             }
00135         }
00136 
00137 #if NET_VERSION_4_5
00138         [MethodImpl(MethodImplOptions.AggressiveInlining)]
00139         #endif
00140 
00141         private void CascadeDown(T node)
00142         {
00143             //aka Heapify-down
00144             T newParent;
00145             int finalQueueIndex = node.QueueIndex;
00146             while (true)
00147             {
00148                 newParent = node;
00149                 int childLeftIndex = 2 * finalQueueIndex;
00150 
00151                 //Check if the left-child is higher-priority than the current node
00152                 if (childLeftIndex > this.Count)
00153                 {
00154                     //This could be placed outside the loop, but then we'd have to check newParent != node twice
00155                     node.QueueIndex = finalQueueIndex;
00156                     this._nodes[finalQueueIndex] = node;
00157                     break;
00158                 }
00159 
00160                 T childLeft = this._nodes[childLeftIndex];
00161                 if (this.HasHigherPriority(childLeft, newParent))
00162                 {
00163                     newParent = childLeft;
00164                 }
00165 
00166                 //Check if the right-child is higher-priority than either the current node or the left child
00167                 int childRightIndex = childLeftIndex + 1;
00168                 if (childRightIndex <= this.Count)
00169                 {
00170                     T childRight = this._nodes[childRightIndex];
00171                     if (this.HasHigherPriority(childRight, newParent))
00172                     {
00173                         newParent = childRight;
00174                     }
00175                 }
00176 
00177                 //If either of the children has higher (smaller) priority, swap and continue cascading
00178                 if (newParent != node)
00179                 {
00180                     //Move new parent to its new index.  node will be moved once, at the end
00181                     //Doing it this way is one less assignment operation than calling Swap()
00182                     this._nodes[finalQueueIndex] = newParent;
00183 
00184                     int temp = newParent.QueueIndex;
00185                     newParent.QueueIndex = finalQueueIndex;
00186                     finalQueueIndex = temp;
00187                 }
00188                 else
00189                 {
00190                     //See note above
00191                     node.QueueIndex = finalQueueIndex;
00192                     this._nodes[finalQueueIndex] = node;
00193                     break;
00194                 }
00195             }
00196         }
00197 
00202 #if NET_VERSION_4_5
00203         [MethodImpl(MethodImplOptions.AggressiveInlining)]
00204 #endif
00205         private bool HasHigherPriority(T higher, T lower)
00206         {
00207             return (higher.Priority < lower.Priority ||
00208                     (Math.Abs(higher.Priority - lower.Priority) < 0.01 && higher.InsertionIndex < lower.InsertionIndex));
00209         }
00210 
00215         public T Dequeue()
00216         {
00217             T returnMe = this._nodes[1];
00218             this.Remove(returnMe);
00219             return returnMe;
00220         }
00221 
00225         public T First
00226         {
00227             get { return this._nodes[1]; }
00228         }
00229 
00235 #if NET_VERSION_4_5
00236         [MethodImpl(MethodImplOptions.AggressiveInlining)]
00237         #endif
00238         public void UpdatePriority(T node, double priority)
00239         {
00240             node.Priority = priority;
00241             this.OnNodeUpdated(node);
00242         }
00243 
00244         private void OnNodeUpdated(T node)
00245         {
00246             //Bubble the updated node up or down as appropriate
00247             int parentIndex = node.QueueIndex / 2;
00248             T parentNode = this._nodes[parentIndex];
00249 
00250             if (parentIndex > 0 && this.HasHigherPriority(node, parentNode))
00251             {
00252                 this.CascadeUp(node);
00253             }
00254             else
00255             {
00256                 //Note that CascadeDown will be called if parentNode == node (that is, node is the root)
00257                 this.CascadeDown(node);
00258             }
00259         }
00260 
00264         public void Remove(T node)
00265         {
00266             if (!this.Contains(node))
00267             {
00268                 return;
00269             }
00270 
00271             if (this.Count <= 1)
00272             {
00273                 this._nodes[1] = null;
00274                 this.Count = 0;
00275                 return;
00276             }
00277 
00278             //Make sure the node is the last node in the queue
00279             bool wasSwapped = false;
00280             T formerLastNode = this._nodes[this.Count];
00281             if (node.QueueIndex != this.Count)
00282             {
00283                 //Swap the node with the last node
00284                 this.Swap(node, formerLastNode);
00285                 wasSwapped = true;
00286             }
00287 
00288             this.Count--;
00289             this._nodes[node.QueueIndex] = null;
00290 
00291             if (wasSwapped)
00292             {
00293                 //Now bubble formerLastNode (which is no longer the last node) up or down as appropriate
00294                 this.OnNodeUpdated(formerLastNode);
00295             }
00296         }
00297 
00298         public IEnumerator<T> GetEnumerator()
00299         {
00300             for (int i = 1; i <= this.Count; i++)
00301                 yield return this._nodes[i];
00302         }
00303 
00304         IEnumerator IEnumerable.GetEnumerator()
00305         {
00306             return this.GetEnumerator();
00307         }
00308 
00313         public bool IsValidQueue()
00314         {
00315             for (int i = 1; i < this._nodes.Length; i++)
00316             {
00317                 if (this._nodes[i] != null)
00318                 {
00319                     int childLeftIndex = 2 * i;
00320                     if (childLeftIndex < this._nodes.Length && this._nodes[childLeftIndex] != null &&
00321                         this.HasHigherPriority(this._nodes[childLeftIndex], this._nodes[i]))
00322                         return false;
00323 
00324                     int childRightIndex = childLeftIndex + 1;
00325                     if (childRightIndex < this._nodes.Length && this._nodes[childRightIndex] != null &&
00326                         this.HasHigherPriority(this._nodes[childRightIndex], this._nodes[i]))
00327                         return false;
00328                 }
00329             }
00330             return true;
00331         }
00332     }
00333 }