SimplePriorityQueue.cs 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Threading.Tasks;
  7. namespace Priority_Queue
  8. {
  9. /// <summary>
  10. /// Credit: https://github.com/BlueRaja/High-Speed-Priority-Queue-for-C-Sharp
  11. /// A simplified priority queue implementation. Is stable, auto-resizes, and thread-safe, at the cost of being slightly slower than
  12. /// FastPriorityQueue
  13. /// </summary>
  14. /// <typeparam name="TItem">The type to enqueue</typeparam>
  15. /// <typeparam name="TPriority">The priority-type to use for nodes. Must extend IComparable&lt;TPriority&gt;</typeparam>
  16. public class SimplePriorityQueue<TItem, TPriority> : IPriorityQueue<TItem, TPriority>
  17. where TPriority : IComparable<TPriority>
  18. {
  19. private class SimpleNode : GenericPriorityQueueNode<TPriority>
  20. {
  21. public TItem Data { get; private set; }
  22. public SimpleNode(TItem data)
  23. {
  24. Data = data;
  25. }
  26. }
  27. private const int INITIAL_QUEUE_SIZE = 10;
  28. private readonly GenericPriorityQueue<SimpleNode, TPriority> _queue;
  29. public SimplePriorityQueue()
  30. {
  31. _queue = new GenericPriorityQueue<SimpleNode, TPriority>(INITIAL_QUEUE_SIZE);
  32. }
  33. /// <summary>
  34. /// Given an item of type T, returns the exist SimpleNode in the queue
  35. /// </summary>
  36. private SimpleNode GetExistingNode(TItem item)
  37. {
  38. var comparer = EqualityComparer<TItem>.Default;
  39. foreach (var node in _queue)
  40. {
  41. if (comparer.Equals(node.Data, item))
  42. {
  43. return node;
  44. }
  45. }
  46. throw new InvalidOperationException("Item cannot be found in queue: " + item);
  47. }
  48. /// <summary>
  49. /// Returns the number of nodes in the queue.
  50. /// O(1)
  51. /// </summary>
  52. public int Count
  53. {
  54. get
  55. {
  56. lock (_queue)
  57. {
  58. return _queue.Count;
  59. }
  60. }
  61. }
  62. /// <summary>
  63. /// Returns the head of the queue, without removing it (use Dequeue() for that).
  64. /// Throws an exception when the queue is empty.
  65. /// O(1)
  66. /// </summary>
  67. public TItem First
  68. {
  69. get
  70. {
  71. lock (_queue)
  72. {
  73. if (_queue.Count <= 0)
  74. {
  75. throw new InvalidOperationException("Cannot call .First on an empty queue");
  76. }
  77. SimpleNode first = _queue.First;
  78. return (first != null ? first.Data : default(TItem));
  79. }
  80. }
  81. }
  82. /// <summary>
  83. /// Removes every node from the queue.
  84. /// O(n)
  85. /// </summary>
  86. public void Clear()
  87. {
  88. lock (_queue)
  89. {
  90. _queue.Clear();
  91. }
  92. }
  93. /// <summary>
  94. /// Returns whether the given item is in the queue.
  95. /// O(n)
  96. /// </summary>
  97. public bool Contains(TItem item)
  98. {
  99. lock (_queue)
  100. {
  101. var comparer = EqualityComparer<TItem>.Default;
  102. foreach (var node in _queue)
  103. {
  104. if (comparer.Equals(node.Data, item))
  105. {
  106. return true;
  107. }
  108. }
  109. return false;
  110. }
  111. }
  112. /// <summary>
  113. /// Removes the head of the queue (node with minimum priority; ties are broken by order of insertion), and returns it.
  114. /// If queue is empty, throws an exception
  115. /// O(log n)
  116. /// </summary>
  117. public bool TryDequeue(out TItem item)
  118. {
  119. lock (_queue)
  120. {
  121. if (_queue.Count <= 0)
  122. {
  123. item = default(TItem);
  124. return false;
  125. }
  126. SimpleNode node;
  127. if (_queue.TryDequeue(out node))
  128. {
  129. item = node.Data;
  130. return true;
  131. }
  132. item = default(TItem);
  133. return false;
  134. }
  135. }
  136. /// <summary>
  137. /// Enqueue a node to the priority queue. Lower values are placed in front. Ties are broken by first-in-first-out.
  138. /// This queue automatically resizes itself, so there's no concern of the queue becoming 'full'.
  139. /// Duplicates are allowed.
  140. /// O(log n)
  141. /// </summary>
  142. public void Enqueue(TItem item, TPriority priority)
  143. {
  144. lock (_queue)
  145. {
  146. SimpleNode node = new SimpleNode(item);
  147. if (_queue.Count == _queue.MaxSize)
  148. {
  149. _queue.Resize(_queue.MaxSize * 2 + 1);
  150. }
  151. _queue.Enqueue(node, priority);
  152. }
  153. }
  154. /// <summary>
  155. /// Removes an item from the queue. The item does not need to be the head of the queue.
  156. /// If the item is not in the queue, an exception is thrown. If unsure, check Contains() first.
  157. /// If multiple copies of the item are enqueued, only the first one is removed.
  158. /// O(n)
  159. /// </summary>
  160. public void Remove(TItem item)
  161. {
  162. lock (_queue)
  163. {
  164. try
  165. {
  166. _queue.Remove(GetExistingNode(item));
  167. }
  168. catch (InvalidOperationException ex)
  169. {
  170. throw new InvalidOperationException("Cannot call Remove() on a node which is not enqueued: " + item, ex);
  171. }
  172. }
  173. }
  174. /// <summary>
  175. /// Call this method to change the priority of an item.
  176. /// Calling this method on a item not in the queue will throw an exception.
  177. /// If the item is enqueued multiple times, only the first one will be updated.
  178. /// (If your requirements are complex enough that you need to enqueue the same item multiple times <i>and</i> be able
  179. /// to update all of them, please wrap your items in a wrapper class so they can be distinguished).
  180. /// O(n)
  181. /// </summary>
  182. public void UpdatePriority(TItem item, TPriority priority)
  183. {
  184. lock (_queue)
  185. {
  186. try
  187. {
  188. SimpleNode updateMe = GetExistingNode(item);
  189. _queue.UpdatePriority(updateMe, priority);
  190. }
  191. catch (InvalidOperationException ex)
  192. {
  193. throw new InvalidOperationException("Cannot call UpdatePriority() on a node which is not enqueued: " + item, ex);
  194. }
  195. }
  196. }
  197. public IEnumerator<TItem> GetEnumerator()
  198. {
  199. List<TItem> queueData = new List<TItem>();
  200. lock (_queue)
  201. {
  202. //Copy to a separate list because we don't want to 'yield return' inside a lock
  203. foreach (var node in _queue)
  204. {
  205. queueData.Add(node.Data);
  206. }
  207. }
  208. return queueData.GetEnumerator();
  209. }
  210. IEnumerator IEnumerable.GetEnumerator()
  211. {
  212. return GetEnumerator();
  213. }
  214. public bool IsValidQueue()
  215. {
  216. lock (_queue)
  217. {
  218. return _queue.IsValidQueue();
  219. }
  220. }
  221. }
  222. /// <summary>
  223. /// A simplified priority queue implementation. Is stable, auto-resizes, and thread-safe, at the cost of being slightly slower than
  224. /// FastPriorityQueue
  225. /// This class is kept here for backwards compatibility. It's recommended you use Simple
  226. /// </summary>
  227. /// <typeparam name="TItem">The type to enqueue</typeparam>
  228. public class SimplePriorityQueue<TItem> : SimplePriorityQueue<TItem, float> { }
  229. }