SimplePriorityQueue.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. namespace IFramework.Queue
  5. {
  6. /// <summary>
  7. /// 简易优先级队列
  8. /// </summary>
  9. /// <typeparam name="TItem">队列元素</typeparam>
  10. /// <typeparam name="TPriority">紧急程度</typeparam>
  11. public class SimplePriorityQueue<TItem, TPriority> : IPriorityQueue<TItem, TPriority> where TPriority : IComparable<TPriority>
  12. {
  13. /// <summary>
  14. /// 简单优先级队列节点
  15. /// </summary>
  16. private class SimpleNode : GenericPriorityQueueNode<TPriority>
  17. {
  18. public TItem Data { get; private set; }
  19. public SimpleNode(TItem data)
  20. {
  21. Data = data;
  22. }
  23. }
  24. private const int INITIAL_QUEUE_SIZE = 10;
  25. private readonly GenericPriorityQueue<SimpleNode, TPriority> _queue;
  26. private readonly Dictionary<TItem, IList<SimpleNode>> _itemToNodesCache;
  27. private readonly IList<SimpleNode> _nullNodesCache;
  28. #region 构造器
  29. /// <summary>
  30. /// Ctor
  31. /// </summary>
  32. public SimplePriorityQueue() : this(Comparer<TPriority>.Default, EqualityComparer<TItem>.Default) { }
  33. /// <summary>
  34. /// Ctor
  35. /// </summary>
  36. /// <param name="priorityComparer">用于比较优先级的TPriority值, 默认为Comparer&lt;TPriority&gt;.default</param>
  37. public SimplePriorityQueue(IComparer<TPriority> priorityComparer) : this(priorityComparer.Compare, EqualityComparer<TItem>.Default) { }
  38. /// <summary>
  39. /// Ctor
  40. /// </summary>
  41. /// <param name="priorityComparer">用于比较优先级的TPriority值</param>
  42. public SimplePriorityQueue(Comparison<TPriority> priorityComparer) : this(priorityComparer, EqualityComparer<TItem>.Default) { }
  43. /// <summary>
  44. /// Ctor
  45. /// </summary>
  46. /// <param name="itemEquality">用于比较TItem值是否相等的方法</param>
  47. public SimplePriorityQueue(IEqualityComparer<TItem> itemEquality) : this(Comparer<TPriority>.Default, itemEquality) { }
  48. /// <summary>
  49. /// Ctor
  50. /// </summary>
  51. /// <param name="priorityComparer">用于比较优先级的TPriority值, 默认为Comparer&lt;TPriority&gt;.default</param>
  52. /// <param name="itemEquality">用于比较TItem值是否相等的方法</param>
  53. public SimplePriorityQueue(IComparer<TPriority> priorityComparer, IEqualityComparer<TItem> itemEquality) : this(priorityComparer.Compare, itemEquality) { }
  54. /// <summary>
  55. /// Ctor
  56. /// </summary>
  57. /// <param name="priorityComparer">用于比较优先级的TPriority值, 默认为Comparer&lt;TPriority&gt;.default</param>
  58. /// <param name="itemEquality">用于比较TItem值是否相等的方法</param>
  59. public SimplePriorityQueue(Comparison<TPriority> priorityComparer, IEqualityComparer<TItem> itemEquality)
  60. {
  61. _queue = new GenericPriorityQueue<SimpleNode, TPriority>(INITIAL_QUEUE_SIZE, priorityComparer);
  62. _itemToNodesCache = new Dictionary<TItem, IList<SimpleNode>>(itemEquality);
  63. _nullNodesCache = new List<SimpleNode>();
  64. }
  65. #endregion
  66. /// <summary>
  67. /// 根据所给的item获取队列中的节点
  68. /// </summary>
  69. /// <returns>匹配的SimpleNode节点</returns>
  70. private SimpleNode GetExistingNode(TItem item)
  71. {
  72. if (item == null)
  73. {
  74. return _nullNodesCache.Count > 0 ? _nullNodesCache[0] : null;
  75. }
  76. IList<SimpleNode> nodes;
  77. if (!_itemToNodesCache.TryGetValue(item, out nodes))
  78. {
  79. return null;
  80. }
  81. return nodes[0];
  82. }
  83. /// <summary>
  84. /// 将节点添加到节点缓存<br />
  85. /// O(1) 或 O(log n)
  86. /// </summary>
  87. /// <param name="node">需要添加到缓存的节点</param>
  88. private void AddToNodeCache(SimpleNode node)
  89. {
  90. if (node.Data == null)
  91. {
  92. _nullNodesCache.Add(node);
  93. return;
  94. }
  95. IList<SimpleNode> nodes;
  96. if (!_itemToNodesCache.TryGetValue(node.Data, out nodes))
  97. {
  98. nodes = new List<SimpleNode>();
  99. _itemToNodesCache[node.Data] = nodes;
  100. }
  101. nodes.Add(node);
  102. }
  103. /// <summary>
  104. /// 将节点从节点缓存中删除<br />
  105. /// O(1) 或 O(log n) 【假设没有重复】
  106. /// </summary>
  107. /// <param name="node">需要从缓存中删除的节点</param>
  108. private void RemoveFromNodeCache(SimpleNode node)
  109. {
  110. if (node.Data == null)
  111. {
  112. _nullNodesCache.Remove(node);
  113. return;
  114. }
  115. IList<SimpleNode> nodes;
  116. if (!_itemToNodesCache.TryGetValue(node.Data, out nodes))
  117. {
  118. return;
  119. }
  120. nodes.Remove(node);
  121. if (nodes.Count == 0)
  122. {
  123. _itemToNodesCache.Remove(node.Data);
  124. }
  125. }
  126. /// <summary>
  127. /// 返回队列里的元素数量<br />
  128. /// O(1)
  129. /// </summary>
  130. public int count
  131. {
  132. get
  133. {
  134. lock (_queue)
  135. {
  136. return _queue.count;
  137. }
  138. }
  139. }
  140. /// <summary>
  141. /// 队列的第一个元素<br />
  142. /// 如果队列为空,则报错<br />
  143. /// O(1)
  144. /// </summary>
  145. public TItem first
  146. {
  147. get
  148. {
  149. lock (_queue)
  150. {
  151. if (_queue.count <= 0)
  152. {
  153. throw new InvalidOperationException("Cannot call .first on an empty queue");
  154. }
  155. return _queue.first.Data;
  156. }
  157. }
  158. }
  159. /// <summary>
  160. /// 清空队列里的所有元素<br />
  161. /// O(n)
  162. /// </summary>
  163. public void Clear()
  164. {
  165. lock (_queue)
  166. {
  167. _queue.Clear();
  168. _itemToNodesCache.Clear();
  169. _nullNodesCache.Clear();
  170. }
  171. }
  172. /// <summary>
  173. /// 判断当前元素是否在队列中<br />
  174. /// O(1)
  175. /// </summary>
  176. /// <param name="item">需要判断的元素</param>
  177. /// <returns>如果队列包含这个元素则返回true 否则为false</returns>
  178. public bool Contains(TItem item)
  179. {
  180. lock (_queue)
  181. {
  182. return item == null ? _nullNodesCache.Count > 0 : _itemToNodesCache.ContainsKey(item);
  183. }
  184. }
  185. /// <summary>
  186. /// 队列头出队<br />
  187. /// 如果队列是空的则抛出异常<br />
  188. /// O(log n)
  189. /// </summary>
  190. /// <returns>出队的元素</returns>
  191. public TItem Dequeue()
  192. {
  193. lock (_queue)
  194. {
  195. if (_queue.count <= 0)
  196. {
  197. throw new InvalidOperationException("Cannot call Dequeue() on an empty queue");
  198. }
  199. SimpleNode node = _queue.Dequeue();
  200. RemoveFromNodeCache(node);
  201. return node.Data;
  202. }
  203. }
  204. /// <summary>
  205. /// 不用lock(_queue)和AddToNodeCache(node)将给定的元素和优先级值入队
  206. /// </summary>
  207. /// <param name="item">入队元素</param>
  208. /// <param name="priority">优先级值</param>
  209. /// <returns>入队的SimpleNode对象</returns>
  210. private SimpleNode EnqueueNoLockOrCache(TItem item, TPriority priority)
  211. {
  212. SimpleNode node = new SimpleNode(item);
  213. if (_queue.count == _queue.capcity)
  214. {
  215. _queue.Resize(_queue.capcity * 2 + 1);
  216. }
  217. _queue.Enqueue(node, priority);
  218. return node;
  219. }
  220. /// <summary>
  221. /// 将节点元素入队,优先级值小的将会排在队列前面,优先级相同的节点以先进先出排序<br />
  222. /// 队列大小会自动调整,所以不用考虑是否会满<br />
  223. /// 重复添加以及空值都是允许的<br />
  224. /// O(log n)
  225. /// </summary>
  226. public void Enqueue(TItem item, TPriority priority)
  227. {
  228. lock (_queue)
  229. {
  230. IList<SimpleNode> nodes;
  231. if (item == null)
  232. {
  233. nodes = _nullNodesCache;
  234. }
  235. else if (!_itemToNodesCache.TryGetValue(item, out nodes))
  236. {
  237. nodes = new List<SimpleNode>();
  238. _itemToNodesCache[item] = nodes;
  239. }
  240. SimpleNode node = EnqueueNoLockOrCache(item, priority);
  241. nodes.Add(node);
  242. }
  243. }
  244. /// <summary>
  245. /// 不重复入队
  246. /// 将一个不在队列中的item及其优先级入队,优先级值小的会被排在前面,相同的优先级以先进先出排序<br />
  247. /// 队列是自动设置大小的,所以不用关心队列是否是满的<br />
  248. /// 空值是允许的<br />
  249. /// O(log n)
  250. /// </summary>
  251. /// <returns> 成功入队返回true,否则返回false;如果在队列中存在则返回false</returns>
  252. public bool EnqueueWithoutDuplicates(TItem item, TPriority priority)
  253. {
  254. lock (_queue)
  255. {
  256. IList<SimpleNode> nodes;
  257. if (item == null)
  258. {
  259. if (_nullNodesCache.Count > 0)
  260. {
  261. return false;
  262. }
  263. nodes = _nullNodesCache;
  264. }
  265. else if (_itemToNodesCache.ContainsKey(item))
  266. {
  267. return false;
  268. }
  269. else
  270. {
  271. nodes = new List<SimpleNode>();
  272. _itemToNodesCache[item] = nodes;
  273. }
  274. SimpleNode node = EnqueueNoLockOrCache(item, priority);
  275. nodes.Add(node);
  276. return true;
  277. }
  278. }
  279. /// <summary>
  280. /// 从队列中删除给定节点元素,这个节点不一定是队列的头<br />
  281. /// 如果节点并未在队列中,结果是未定义的,如果不确定,则先使用Contains()确认<br />
  282. /// 如果有多个副本加入到队列中,则删除匹配的第一个<br />
  283. /// O(log n)
  284. /// </summary>
  285. public void Remove(TItem item)
  286. {
  287. lock (_queue)
  288. {
  289. SimpleNode removeMe;
  290. IList<SimpleNode> nodes;
  291. if (item == null)
  292. {
  293. if (_nullNodesCache.Count == 0)
  294. {
  295. throw new InvalidOperationException($"Cannot call Remove() on a node which is not enqueued: {item}");
  296. }
  297. removeMe = _nullNodesCache[0];
  298. nodes = _nullNodesCache;
  299. }
  300. else
  301. {
  302. if (!_itemToNodesCache.TryGetValue(item, out nodes))
  303. {
  304. throw new InvalidOperationException($"Cannot call Remove() on a node which is not enqueued: {item}");
  305. }
  306. removeMe = nodes[0];
  307. if (nodes.Count == 1)
  308. {
  309. _itemToNodesCache.Remove(item);
  310. }
  311. }
  312. _queue.Remove(removeMe);
  313. nodes.Remove(removeMe);
  314. }
  315. }
  316. /// <summary>
  317. /// 更改队列里某个节点的优先级<br />
  318. /// 对不在这个队列中的节点元素调用这个方法会抛出异常<br />
  319. /// 如果item多次入队,只有第一个item会被更新<br />
  320. /// (如果需要同一时间多次入队且可以同时将他们更新,请将你的items放入包装类中来区分它们)<br />
  321. /// O(log n)
  322. /// </summary>
  323. public void UpdatePriority(TItem item, TPriority priority)
  324. {
  325. lock (_queue)
  326. {
  327. SimpleNode updateMe = GetExistingNode(item);
  328. if (updateMe == null)
  329. {
  330. throw new InvalidOperationException("Cannot call UpdatePriority() on a node which is not enqueued: " + item);
  331. }
  332. _queue.UpdatePriority(updateMe, priority);
  333. }
  334. }
  335. /// <summary>
  336. /// 返回给定Item的TPriority值
  337. /// 对不在队列中的节点元素调用此方法会抛出异常
  338. /// 如果item多次入队,只有第一个的优先级值会被返回<br />
  339. /// (如果需要同一时间多次入队且可以同时将他们更新,请将你的items放入包装类中来区分它们)<br />
  340. /// O(1)
  341. /// </summary>
  342. /// <returns>对应的优先级值</returns>
  343. public TPriority GetPriority(TItem item)
  344. {
  345. lock (_queue)
  346. {
  347. SimpleNode findMe = GetExistingNode(item);
  348. if (findMe == null)
  349. {
  350. throw new InvalidOperationException($"Cannot call GetPriority() on a node which is not enqueued: {item}");
  351. }
  352. return findMe.priority;
  353. }
  354. }
  355. #region Try* methods for multithreading
  356. /// Get the head of the queue, without removing it (use TryDequeue() for that).
  357. /// Useful for multi-threading, where the queue may become empty between calls to Contains() and First
  358. /// Returns true if successful, false otherwise
  359. /// O(1)
  360. public bool TryFirst(out TItem first)
  361. {
  362. if (_queue.count > 0)
  363. {
  364. lock (_queue)
  365. {
  366. if (_queue.count > 0)
  367. {
  368. first = _queue.first.Data;
  369. return true;
  370. }
  371. }
  372. }
  373. first = default(TItem);
  374. return false;
  375. }
  376. /// <summary>
  377. /// Removes the head of the queue (node with minimum priority; ties are broken by order of insertion), and sets it to first.
  378. /// Useful for multi-threading, where the queue may become empty between calls to Contains() and Dequeue()
  379. /// Returns true if successful; false if queue was empty
  380. /// O(log n)
  381. /// </summary>
  382. public bool TryDequeue(out TItem first)
  383. {
  384. if (_queue.count > 0)
  385. {
  386. lock (_queue)
  387. {
  388. if (_queue.count > 0)
  389. {
  390. SimpleNode node = _queue.Dequeue();
  391. first = node.Data;
  392. RemoveFromNodeCache(node);
  393. return true;
  394. }
  395. }
  396. }
  397. first = default(TItem);
  398. return false;
  399. }
  400. /// <summary>
  401. /// Attempts to remove an item from the queue. The item does not need to be the head of the queue.
  402. /// Useful for multi-threading, where the queue may become empty between calls to Contains() and Remove()
  403. /// Returns true if the item was successfully removed, false if it wasn't in the queue.
  404. /// If multiple copies of the item are enqueued, only the first one is removed.
  405. /// O(log n)
  406. /// </summary>
  407. public bool TryRemove(TItem item)
  408. {
  409. lock (_queue)
  410. {
  411. SimpleNode removeMe;
  412. IList<SimpleNode> nodes;
  413. if (item == null)
  414. {
  415. if (_nullNodesCache.Count == 0)
  416. {
  417. return false;
  418. }
  419. removeMe = _nullNodesCache[0];
  420. nodes = _nullNodesCache;
  421. }
  422. else
  423. {
  424. if (!_itemToNodesCache.TryGetValue(item, out nodes))
  425. {
  426. return false;
  427. }
  428. removeMe = nodes[0];
  429. if (nodes.Count == 1)
  430. {
  431. _itemToNodesCache.Remove(item);
  432. }
  433. }
  434. _queue.Remove(removeMe);
  435. nodes.Remove(removeMe);
  436. return true;
  437. }
  438. }
  439. /// <summary>
  440. /// Call this method to change the priority of an item.
  441. /// Useful for multi-threading, where the queue may become empty between calls to Contains() and UpdatePriority()
  442. /// If the item is enqueued multiple times, only the first one will be updated.
  443. /// (If your requirements are complex enough that you need to enqueue the same item multiple times <i>and</i> be able
  444. /// to update all of them, please wrap your items in a wrapper class so they can be distinguished).
  445. /// Returns true if the item priority was updated, false otherwise.
  446. /// O(log n)
  447. /// </summary>
  448. public bool TryUpdatePriority(TItem item, TPriority priority)
  449. {
  450. lock (_queue)
  451. {
  452. SimpleNode updateMe = GetExistingNode(item);
  453. if (updateMe == null)
  454. {
  455. return false;
  456. }
  457. _queue.UpdatePriority(updateMe, priority);
  458. return true;
  459. }
  460. }
  461. /// <summary>
  462. /// Attempt to get the priority of the given item.
  463. /// Useful for multi-threading, where the queue may become empty between calls to Contains() and GetPriority()
  464. /// If the item is enqueued multiple times, only the priority of the first will be returned.
  465. /// (If your requirements are complex enough that you need to enqueue the same item multiple times <i>and</i> be able
  466. /// to query all their priorities, please wrap your items in a wrapper class so they can be distinguished).
  467. /// Returns true if the item was found in the queue, false otherwise
  468. /// O(1)
  469. /// </summary>
  470. public bool TryGetPriority(TItem item, out TPriority priority)
  471. {
  472. lock (_queue)
  473. {
  474. SimpleNode findMe = GetExistingNode(item);
  475. if (findMe == null)
  476. {
  477. priority = default(TPriority);
  478. return false;
  479. }
  480. priority = findMe.priority;
  481. return true;
  482. }
  483. }
  484. #endregion
  485. /// <summary>
  486. /// 获取迭代器
  487. /// </summary>
  488. /// <returns>迭代器</returns>
  489. public IEnumerator<TItem> GetEnumerator()
  490. {
  491. List<TItem> queueData = new List<TItem>();
  492. lock (_queue)
  493. {
  494. //将队列里的所有item放到单独的列表,因为我们不希望在锁中yield return
  495. foreach (var node in _queue)
  496. {
  497. queueData.Add(node.Data);
  498. }
  499. }
  500. return queueData.GetEnumerator();
  501. }
  502. IEnumerator IEnumerable.GetEnumerator()
  503. {
  504. return GetEnumerator();
  505. }
  506. }
  507. /// <summary>
  508. /// A simplified priority queue implementation. Is stable, auto-resizes, and thread-safe, at the cost of being slightly slower than
  509. /// FastPriorityQueue
  510. /// This class is kept here for backwards compatibility. It's recommended you use SimplePriorityQueue&lt;TItem, TPriority&gt;
  511. /// </summary>
  512. /// <typeparam name="TItem">The type to enqueue</typeparam>
  513. public class SimplePriorityQueue<TItem> : SimplePriorityQueue<TItem, float>
  514. {
  515. /// <summary>
  516. /// Instantiate a new Priority Queue
  517. /// </summary>
  518. public SimplePriorityQueue() { }
  519. /// <summary>
  520. /// Instantiate a new Priority Queue
  521. /// </summary>
  522. /// <param name="comparer">The comparer used to compare priority values. Defaults to Comparer&lt;float&gt;.default</param>
  523. public SimplePriorityQueue(IComparer<float> comparer) : base(comparer) { }
  524. /// <summary>
  525. /// Instantiate a new Priority Queue
  526. /// </summary>
  527. /// <param name="comparer">The comparison function to use to compare priority values</param>
  528. public SimplePriorityQueue(Comparison<float> comparer) : base(comparer) { }
  529. }
  530. }