SimplePriorityQueue.cs 8.0 KB

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