PageRenderTime 47ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/Utilities/Collections/BinaryPriorityQueue.cs

#
C# | 574 lines | 354 code | 51 blank | 169 comment | 21 complexity | 46c2b8cef212e2d9d4d73c9702884f9c MD5 | raw file
Possible License(s): Apache-2.0
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using Delta.Utilities.Helpers;
  5. using NUnit.Framework;
  6. namespace Delta.Utilities.Collections
  7. {
  8. /// <summary>
  9. /// Binary priority queue, enumerating will not return the sorted list,
  10. /// just use Push to add new elements and Pop to get the lowest element.
  11. /// </summary>
  12. public class BinaryPriorityQueue<T>
  13. : ICollection, IEnumerable<T>, ICloneable<BinaryPriorityQueue<T>>
  14. {
  15. #region Count (Public)
  16. /// <summary>
  17. /// Count
  18. /// </summary>
  19. /// <typeparam name="T">T</typeparam>
  20. public int Count
  21. {
  22. get
  23. {
  24. return innerList.Count;
  25. }
  26. }
  27. #endregion
  28. #region IsSynchronized (Public)
  29. /// <summary>
  30. /// Is synchronized
  31. /// </summary>
  32. /// <typeparam name="T">T</typeparam>
  33. public bool IsSynchronized
  34. {
  35. get
  36. {
  37. return true; // innerList.IsSynchronized;
  38. }
  39. }
  40. #endregion
  41. #region SyncRoot (Public)
  42. /// <summary>
  43. /// Sync root
  44. /// </summary>
  45. /// <typeparam name="T">T</typeparam>
  46. public object SyncRoot
  47. {
  48. get
  49. {
  50. return this;
  51. }
  52. }
  53. #endregion
  54. #region IsReadOnly (Public)
  55. /// <summary>
  56. /// Is this queue read only? Will always return false.
  57. /// </summary>
  58. public bool IsReadOnly
  59. {
  60. get
  61. {
  62. return false;
  63. }
  64. }
  65. #endregion
  66. #region IsFixedSize (Public)
  67. /// <summary>
  68. /// Is this queue fixed size? Will always return false.
  69. /// </summary>
  70. public bool IsFixedSize
  71. {
  72. get
  73. {
  74. return false;
  75. }
  76. }
  77. #endregion
  78. #region Protected
  79. #region innerList (Protected)
  80. /// <summary>
  81. /// Inner list
  82. /// </summary>
  83. /// <typeparam name="T">T</typeparam>
  84. protected List<T> innerList = new List<T>();
  85. #endregion
  86. #region comparer (Protected)
  87. /// <summary>
  88. /// Comparer
  89. /// </summary>
  90. /// <typeparam name="T">T</typeparam>
  91. protected IComparer comparer;
  92. #endregion
  93. #endregion
  94. #region Private
  95. #region Item (Private)
  96. /// <summary>
  97. /// Indexer for this queue, works like a list.
  98. /// </summary>
  99. /// <returns>Object</returns>
  100. private T this[int index]
  101. {
  102. get
  103. {
  104. return innerList[index];
  105. }
  106. set
  107. {
  108. innerList[index] = value;
  109. Update(index);
  110. }
  111. }
  112. #endregion
  113. #endregion
  114. #region Constructors
  115. /// <summary>
  116. /// Binary priority queue
  117. /// </summary>
  118. public BinaryPriorityQueue()
  119. : this(Comparer<T>.Default)
  120. {
  121. }
  122. /// <summary>
  123. /// Binary priority queue
  124. /// </summary>
  125. /// <param name="setComparer">Comparer for this queue</param>
  126. public BinaryPriorityQueue(IComparer setComparer)
  127. {
  128. comparer = setComparer;
  129. }
  130. /// <summary>
  131. /// Binary priority queue
  132. /// </summary>
  133. /// <param name="initialCapacity">Initial capacity</param>
  134. public BinaryPriorityQueue(int initialCapacity)
  135. : this(Comparer<T>.Default, initialCapacity)
  136. {
  137. }
  138. /// <summary>
  139. /// Binary priority queue
  140. /// </summary>
  141. /// <param name="setComparer">Comparer for this queue</param>
  142. /// <param name="initialCapacity">Initial capacity</param>
  143. public BinaryPriorityQueue(IComparer setComparer, int initialCapacity)
  144. {
  145. comparer = setComparer;
  146. innerList.Capacity = initialCapacity;
  147. }
  148. /// <summary>
  149. /// Create binary priority queue
  150. /// </summary>
  151. /// <param name="setCoreList">Set core list</param>
  152. /// <param name="setComparer">Set comparer</param>
  153. /// <param name="copyList">Copy list</param>
  154. protected BinaryPriorityQueue(
  155. List<T> setCoreList, IComparer setComparer, bool copyList)
  156. {
  157. if (copyList)
  158. {
  159. innerList = new List<T>(setCoreList);
  160. }
  161. else
  162. {
  163. innerList = setCoreList;
  164. }
  165. comparer = setComparer;
  166. }
  167. /// <summary>
  168. /// Create binary priority queue
  169. /// </summary>
  170. /// <param name="setCoreList">Set core list</param>
  171. /// <param name="setComparer">Set comparer</param>
  172. protected BinaryPriorityQueue(
  173. List<T> setCoreList, IComparer setComparer)
  174. : this(setCoreList, setComparer, true)
  175. {
  176. }
  177. /// <summary>
  178. /// Create binary priority queue
  179. /// </summary>
  180. /// <param name="setCoreList">Set core list</param>
  181. protected BinaryPriorityQueue(List<T> setCoreList)
  182. : this(setCoreList, Comparer<T>.Default, true)
  183. {
  184. }
  185. #endregion
  186. #region ICloneable<BinaryPriorityQueue<T>> Members
  187. /// <summary>
  188. /// Clone this BinaryPriorityQueue (will copy all data, inner list and
  189. /// comparer to a new object).
  190. /// </summary>
  191. /// <returns>Cloned BinaryPriorityQueue</returns>
  192. public BinaryPriorityQueue<T> Clone()
  193. {
  194. return new BinaryPriorityQueue<T>(innerList, comparer, true);
  195. }
  196. #endregion
  197. #region ICollection Members
  198. /// <summary>
  199. /// Copy to
  200. /// </summary>
  201. /// <param name="array">Array</param>
  202. /// <param name="index">Index</param>
  203. public void CopyTo(Array array, int index)
  204. {
  205. //innerList.CopyTo(array, index);
  206. throw new ArgumentException("array",
  207. "Array must be just an array of type " + typeof(T) + " for CopyTo");
  208. }
  209. #endregion
  210. #region IEnumerable Members
  211. /// <summary>
  212. /// IEnumerable. get enumerator
  213. /// </summary>
  214. /// <returns>IEnumerator</returns>
  215. IEnumerator IEnumerable.GetEnumerator()
  216. {
  217. return innerList.GetEnumerator();
  218. }
  219. #endregion
  220. #region IEnumerable<T> Members
  221. /// <summary>
  222. /// Get enumerator
  223. /// </summary>
  224. /// <returns>IEnumerator</returns>
  225. public IEnumerator<T> GetEnumerator()
  226. {
  227. return innerList.GetEnumerator();
  228. }
  229. #endregion
  230. #region Push (Public)
  231. /// <summary>
  232. /// Push an object onto the PQ
  233. /// </summary>
  234. /// <param name="addObj">The new object</param>
  235. /// <returns>The index in the list where the object is _now_.
  236. /// This will change when objects are taken from or put onto
  237. /// the PQ.</returns>
  238. public int Push(T addObj)
  239. {
  240. int ret = innerList.Count, p2;
  241. innerList.Add(addObj); // E[ret] = addObj
  242. do
  243. {
  244. if (ret == 0)
  245. {
  246. break;
  247. }
  248. p2 = (ret - 1) / 2;
  249. if (OnCompare(ret, p2) < 0)
  250. {
  251. SwitchElements(ret, p2);
  252. ret = p2;
  253. }
  254. else
  255. {
  256. break;
  257. }
  258. } while (true);
  259. return ret;
  260. }
  261. #endregion
  262. #region Pop (Public)
  263. /// <summary>
  264. /// Get the smallest object and remove it.
  265. /// </summary>
  266. /// <returns>The smallest object</returns>
  267. public T Pop()
  268. {
  269. T result = innerList[0];
  270. int p = 0, p1, p2, pn;
  271. innerList[0] = innerList[innerList.Count - 1];
  272. innerList.RemoveAt(innerList.Count - 1);
  273. do
  274. {
  275. pn = p;
  276. p1 = 2 * p + 1;
  277. p2 = 2 * p + 2;
  278. // Left tree is smaller
  279. if (innerList.Count > p1 &&
  280. OnCompare(p, p1) > 0)
  281. {
  282. p = p1;
  283. }
  284. // Rights tree is smaller
  285. if (innerList.Count > p2 &&
  286. OnCompare(p, p2) > 0)
  287. {
  288. p = p2;
  289. }
  290. if (p == pn)
  291. {
  292. break;
  293. }
  294. SwitchElements(p, pn);
  295. } while (true);
  296. return result;
  297. }
  298. #endregion
  299. #region Peek (Public)
  300. /// <summary>
  301. /// Get the smallest object without removing it.
  302. /// </summary>
  303. /// <returns>The smallest object</returns>
  304. public T Peek()
  305. {
  306. if (innerList.Count > 0)
  307. {
  308. return innerList[0];
  309. }
  310. return default(T);
  311. }
  312. #endregion
  313. #region Contains (Public)
  314. /// <summary>
  315. /// Contains
  316. /// </summary>
  317. /// <param name="value">Value</param>
  318. /// <returns>True if the value was found, false otherwise</returns>
  319. public bool Contains(T value)
  320. {
  321. return innerList.Contains(value);
  322. }
  323. #endregion
  324. #region Clear (Public)
  325. /// <summary>
  326. /// Clear
  327. /// </summary>
  328. public void Clear()
  329. {
  330. innerList.Clear();
  331. }
  332. #endregion
  333. #region CopyTo (Public)
  334. /// <summary>
  335. /// Copy all values to target array, which must be big enough.
  336. /// </summary>
  337. /// <param name="array">Array to copy into</param>
  338. /// <param name="index">Index to start copying into</param>
  339. public void CopyTo(T[] array, int index)
  340. {
  341. innerList.CopyTo(array, index);
  342. }
  343. #endregion
  344. #region Update (Public)
  345. /// <summary>
  346. /// Notify the PQ that the object at position i has changed and the PQ
  347. /// needs to restore order. Since you dont have access to any indexes
  348. /// (except by using the explicit IList.this) you should not call this
  349. /// function without knowing exactly what you do.
  350. /// </summary>
  351. /// <param name="index">The index of the changed object.</param>
  352. public void Update(int index)
  353. {
  354. int p = index, pn;
  355. int p1, p2;
  356. // Process
  357. do
  358. {
  359. if (p == 0)
  360. {
  361. break;
  362. }
  363. p2 = (p - 1) / 2;
  364. if (OnCompare(p, p2) < 0)
  365. {
  366. SwitchElements(p, p2);
  367. p = p2;
  368. }
  369. else
  370. {
  371. break;
  372. }
  373. } while (true);
  374. if (p < index)
  375. {
  376. return;
  377. }
  378. // Process
  379. do
  380. {
  381. pn = p;
  382. p1 = 2 * p + 1;
  383. p2 = 2 * p + 2;
  384. // Left side smaller?
  385. if (innerList.Count > p1 &&
  386. OnCompare(p, p1) > 0)
  387. {
  388. p = p1;
  389. }
  390. // Right side smaller?
  391. if (innerList.Count > p2 &&
  392. OnCompare(p, p2) > 0)
  393. {
  394. p = p2;
  395. }
  396. if (p == pn)
  397. {
  398. break;
  399. }
  400. SwitchElements(p, pn);
  401. } while (true);
  402. }
  403. #endregion
  404. #region Methods (Private)
  405. #region SwitchElements
  406. /// <summary>
  407. /// Swtich elements
  408. /// </summary>
  409. /// <param name="index1">Index 1</param>
  410. /// <param name="index2">Index 2</param>
  411. protected void SwitchElements(int index1, int index2)
  412. {
  413. T temp = innerList[index1];
  414. innerList[index1] = innerList[index2];
  415. innerList[index2] = temp;
  416. }
  417. #endregion
  418. #region OnCompare
  419. /// <summary>
  420. /// On compare
  421. /// </summary>
  422. /// <param name="index1">Index 1</param>
  423. /// <param name="index2">Index 2</param>
  424. /// <returns>Int</returns>
  425. protected virtual int OnCompare(int index1, int index2)
  426. {
  427. return comparer.Compare(innerList[index1], innerList[index2]);
  428. }
  429. #endregion
  430. #region Add
  431. /// <summary>
  432. /// IList. add
  433. /// </summary>
  434. /// <param name="value">Object to add</param>
  435. /// <returns>Int</returns>
  436. private int Add(T value)
  437. {
  438. return Push(value);
  439. }
  440. #endregion
  441. #region RemoveAt
  442. /// <summary>
  443. /// IList. remove at
  444. /// </summary>
  445. /// <param name="index">Index</param>
  446. private void RemoveAt(int index)
  447. {
  448. throw new NotSupportedException();
  449. }
  450. #endregion
  451. #region Insert
  452. /// <summary>
  453. /// IList. insert
  454. /// </summary>
  455. /// <param name="index">Index</param>
  456. /// <param name="value">Value</param>
  457. private void Insert(int index, T value)
  458. {
  459. throw new NotSupportedException();
  460. }
  461. #endregion
  462. #region Remove
  463. /// <summary>
  464. /// IList. remove
  465. /// </summary>
  466. /// <param name="value">Value</param>
  467. private void Remove(T value)
  468. {
  469. throw new NotSupportedException();
  470. }
  471. #endregion
  472. #region IndexOf
  473. /// <summary>
  474. /// IList. index of
  475. /// </summary>
  476. /// <param name="value">Value</param>
  477. /// <returns>Int</returns>
  478. private int IndexOf(T value)
  479. {
  480. throw new NotSupportedException();
  481. }
  482. #endregion
  483. #endregion
  484. }
  485. /// <summary>
  486. /// Binary priority queue tests, must be declared outside of a generic class.
  487. /// </summary>
  488. public class BinaryPriorityQueueTests
  489. {
  490. #region TestBinaryPriorityQueue (Static)
  491. /// <summary>
  492. /// Test BinaryPriorityQueue. Note: Too slow for a dynamic unit test.
  493. /// </summary>
  494. [Test]
  495. public static void TestBinaryPriorityQueue()
  496. {
  497. BinaryPriorityQueue<int>
  498. queue = new BinaryPriorityQueue<int>();
  499. // Push entries
  500. queue.Push(1);
  501. queue.Push(5);
  502. queue.Push(9);
  503. queue.Push(2);
  504. Assert.Equal(4, queue.Count);
  505. queue.Push(5);
  506. Assert.Equal(5, queue.Count);
  507. // Check entries
  508. Assert.Equal(true, queue.Contains(5));
  509. Assert.Equal(false, queue.Contains(6));
  510. // Check whole content (list is binary and looks strange ^^)
  511. Assert.Equal("1, 2, 9, 5, 5",
  512. ArrayHelper.ToArray(queue).Write());
  513. // And pop everything (should be sorted)
  514. Assert.Equal(1, queue.Pop());
  515. Assert.Equal(2, queue.Pop());
  516. Assert.Equal(5, queue.Pop());
  517. Assert.Equal(5, queue.Pop());
  518. Assert.Equal(9, queue.Pop());
  519. Assert.Equal(0, queue.Count);
  520. }
  521. #endregion
  522. }
  523. }