PageRenderTime 125ms CodeModel.GetById 51ms app.highlight 16ms RepoModel.GetById 54ms app.codeStats 0ms

/Utilities/Collections/BinaryPriorityQueue.cs

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