/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
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using Delta.Utilities.Helpers;
- using NUnit.Framework;
-
- namespace Delta.Utilities.Collections
- {
- /// <summary>
- /// Binary priority queue, enumerating will not return the sorted list,
- /// just use Push to add new elements and Pop to get the lowest element.
- /// </summary>
- public class BinaryPriorityQueue<T>
- : ICollection, IEnumerable<T>, ICloneable<BinaryPriorityQueue<T>>
- {
- #region Count (Public)
- /// <summary>
- /// Count
- /// </summary>
- /// <typeparam name="T">T</typeparam>
- public int Count
- {
- get
- {
- return innerList.Count;
- }
- }
- #endregion
-
- #region IsSynchronized (Public)
- /// <summary>
- /// Is synchronized
- /// </summary>
- /// <typeparam name="T">T</typeparam>
- public bool IsSynchronized
- {
- get
- {
- return true; // innerList.IsSynchronized;
- }
- }
- #endregion
-
- #region SyncRoot (Public)
- /// <summary>
- /// Sync root
- /// </summary>
- /// <typeparam name="T">T</typeparam>
- public object SyncRoot
- {
- get
- {
- return this;
- }
- }
- #endregion
-
- #region IsReadOnly (Public)
- /// <summary>
- /// Is this queue read only? Will always return false.
- /// </summary>
- public bool IsReadOnly
- {
- get
- {
- return false;
- }
- }
- #endregion
-
- #region IsFixedSize (Public)
- /// <summary>
- /// Is this queue fixed size? Will always return false.
- /// </summary>
- public bool IsFixedSize
- {
- get
- {
- return false;
- }
- }
- #endregion
-
- #region Protected
-
- #region innerList (Protected)
- /// <summary>
- /// Inner list
- /// </summary>
- /// <typeparam name="T">T</typeparam>
- protected List<T> innerList = new List<T>();
- #endregion
-
- #region comparer (Protected)
- /// <summary>
- /// Comparer
- /// </summary>
- /// <typeparam name="T">T</typeparam>
- protected IComparer comparer;
- #endregion
-
- #endregion
-
- #region Private
-
- #region Item (Private)
- /// <summary>
- /// Indexer for this queue, works like a list.
- /// </summary>
- /// <returns>Object</returns>
- private T this[int index]
- {
- get
- {
- return innerList[index];
- }
- set
- {
- innerList[index] = value;
- Update(index);
- }
- }
- #endregion
-
- #endregion
-
- #region Constructors
- /// <summary>
- /// Binary priority queue
- /// </summary>
- public BinaryPriorityQueue()
- : this(Comparer<T>.Default)
- {
- }
-
- /// <summary>
- /// Binary priority queue
- /// </summary>
- /// <param name="setComparer">Comparer for this queue</param>
- public BinaryPriorityQueue(IComparer setComparer)
- {
- comparer = setComparer;
- }
-
- /// <summary>
- /// Binary priority queue
- /// </summary>
- /// <param name="initialCapacity">Initial capacity</param>
- public BinaryPriorityQueue(int initialCapacity)
- : this(Comparer<T>.Default, initialCapacity)
- {
- }
-
- /// <summary>
- /// Binary priority queue
- /// </summary>
- /// <param name="setComparer">Comparer for this queue</param>
- /// <param name="initialCapacity">Initial capacity</param>
- public BinaryPriorityQueue(IComparer setComparer, int initialCapacity)
- {
- comparer = setComparer;
- innerList.Capacity = initialCapacity;
- }
-
- /// <summary>
- /// Create binary priority queue
- /// </summary>
- /// <param name="setCoreList">Set core list</param>
- /// <param name="setComparer">Set comparer</param>
- /// <param name="copyList">Copy list</param>
- protected BinaryPriorityQueue(
- List<T> setCoreList, IComparer setComparer, bool copyList)
- {
- if (copyList)
- {
- innerList = new List<T>(setCoreList);
- }
- else
- {
- innerList = setCoreList;
- }
- comparer = setComparer;
- }
-
- /// <summary>
- /// Create binary priority queue
- /// </summary>
- /// <param name="setCoreList">Set core list</param>
- /// <param name="setComparer">Set comparer</param>
- protected BinaryPriorityQueue(
- List<T> setCoreList, IComparer setComparer)
- : this(setCoreList, setComparer, true)
- {
- }
-
- /// <summary>
- /// Create binary priority queue
- /// </summary>
- /// <param name="setCoreList">Set core list</param>
- protected BinaryPriorityQueue(List<T> setCoreList)
- : this(setCoreList, Comparer<T>.Default, true)
- {
- }
- #endregion
-
- #region ICloneable<BinaryPriorityQueue<T>> Members
- /// <summary>
- /// Clone this BinaryPriorityQueue (will copy all data, inner list and
- /// comparer to a new object).
- /// </summary>
- /// <returns>Cloned BinaryPriorityQueue</returns>
- public BinaryPriorityQueue<T> Clone()
- {
- return new BinaryPriorityQueue<T>(innerList, comparer, true);
- }
- #endregion
-
- #region ICollection Members
- /// <summary>
- /// Copy to
- /// </summary>
- /// <param name="array">Array</param>
- /// <param name="index">Index</param>
- public void CopyTo(Array array, int index)
- {
- //innerList.CopyTo(array, index);
- throw new ArgumentException("array",
- "Array must be just an array of type " + typeof(T) + " for CopyTo");
- }
- #endregion
-
- #region IEnumerable Members
- /// <summary>
- /// IEnumerable. get enumerator
- /// </summary>
- /// <returns>IEnumerator</returns>
- IEnumerator IEnumerable.GetEnumerator()
- {
- return innerList.GetEnumerator();
- }
- #endregion
-
- #region IEnumerable<T> Members
- /// <summary>
- /// Get enumerator
- /// </summary>
- /// <returns>IEnumerator</returns>
- public IEnumerator<T> GetEnumerator()
- {
- return innerList.GetEnumerator();
- }
- #endregion
-
- #region Push (Public)
- /// <summary>
- /// Push an object onto the PQ
- /// </summary>
- /// <param name="addObj">The new object</param>
- /// <returns>The index in the list where the object is _now_.
- /// This will change when objects are taken from or put onto
- /// the PQ.</returns>
- public int Push(T addObj)
- {
- int ret = innerList.Count, p2;
- innerList.Add(addObj); // E[ret] = addObj
- do
- {
- if (ret == 0)
- {
- break;
- }
- p2 = (ret - 1) / 2;
- if (OnCompare(ret, p2) < 0)
- {
- SwitchElements(ret, p2);
- ret = p2;
- }
- else
- {
- break;
- }
- } while (true);
-
- return ret;
- }
- #endregion
-
- #region Pop (Public)
- /// <summary>
- /// Get the smallest object and remove it.
- /// </summary>
- /// <returns>The smallest object</returns>
- public T Pop()
- {
- T result = innerList[0];
- int p = 0, p1, p2, pn;
- innerList[0] = innerList[innerList.Count - 1];
- innerList.RemoveAt(innerList.Count - 1);
- do
- {
- pn = p;
- p1 = 2 * p + 1;
- p2 = 2 * p + 2;
- // Left tree is smaller
- if (innerList.Count > p1 &&
- OnCompare(p, p1) > 0)
- {
- p = p1;
- }
- // Rights tree is smaller
- if (innerList.Count > p2 &&
- OnCompare(p, p2) > 0)
- {
- p = p2;
- }
-
- if (p == pn)
- {
- break;
- }
- SwitchElements(p, pn);
- } while (true);
-
- return result;
- }
- #endregion
-
- #region Peek (Public)
- /// <summary>
- /// Get the smallest object without removing it.
- /// </summary>
- /// <returns>The smallest object</returns>
- public T Peek()
- {
- if (innerList.Count > 0)
- {
- return innerList[0];
- }
- return default(T);
- }
- #endregion
-
- #region Contains (Public)
- /// <summary>
- /// Contains
- /// </summary>
- /// <param name="value">Value</param>
- /// <returns>True if the value was found, false otherwise</returns>
- public bool Contains(T value)
- {
- return innerList.Contains(value);
- }
- #endregion
-
- #region Clear (Public)
- /// <summary>
- /// Clear
- /// </summary>
- public void Clear()
- {
- innerList.Clear();
- }
- #endregion
-
- #region CopyTo (Public)
- /// <summary>
- /// Copy all values to target array, which must be big enough.
- /// </summary>
- /// <param name="array">Array to copy into</param>
- /// <param name="index">Index to start copying into</param>
- public void CopyTo(T[] array, int index)
- {
- innerList.CopyTo(array, index);
- }
- #endregion
-
- #region Update (Public)
- /// <summary>
- /// Notify the PQ that the object at position i has changed and the PQ
- /// needs to restore order. Since you dont have access to any indexes
- /// (except by using the explicit IList.this) you should not call this
- /// function without knowing exactly what you do.
- /// </summary>
- /// <param name="index">The index of the changed object.</param>
- public void Update(int index)
- {
- int p = index, pn;
- int p1, p2;
-
- // Process
- do
- {
- if (p == 0)
- {
- break;
- }
- p2 = (p - 1) / 2;
- if (OnCompare(p, p2) < 0)
- {
- SwitchElements(p, p2);
- p = p2;
- }
- else
- {
- break;
- }
- } while (true);
-
- if (p < index)
- {
- return;
- }
-
- // Process
- do
- {
- pn = p;
- p1 = 2 * p + 1;
- p2 = 2 * p + 2;
- // Left side smaller?
- if (innerList.Count > p1 &&
- OnCompare(p, p1) > 0)
- {
- p = p1;
- }
- // Right side smaller?
- if (innerList.Count > p2 &&
- OnCompare(p, p2) > 0)
- {
- p = p2;
- }
-
- if (p == pn)
- {
- break;
- }
- SwitchElements(p, pn);
- } while (true);
- }
- #endregion
-
- #region Methods (Private)
-
- #region SwitchElements
- /// <summary>
- /// Swtich elements
- /// </summary>
- /// <param name="index1">Index 1</param>
- /// <param name="index2">Index 2</param>
- protected void SwitchElements(int index1, int index2)
- {
- T temp = innerList[index1];
- innerList[index1] = innerList[index2];
- innerList[index2] = temp;
- }
- #endregion
-
- #region OnCompare
- /// <summary>
- /// On compare
- /// </summary>
- /// <param name="index1">Index 1</param>
- /// <param name="index2">Index 2</param>
- /// <returns>Int</returns>
- protected virtual int OnCompare(int index1, int index2)
- {
- return comparer.Compare(innerList[index1], innerList[index2]);
- }
- #endregion
-
- #region Add
- /// <summary>
- /// IList. add
- /// </summary>
- /// <param name="value">Object to add</param>
- /// <returns>Int</returns>
- private int Add(T value)
- {
- return Push(value);
- }
- #endregion
-
- #region RemoveAt
- /// <summary>
- /// IList. remove at
- /// </summary>
- /// <param name="index">Index</param>
- private void RemoveAt(int index)
- {
- throw new NotSupportedException();
- }
- #endregion
-
- #region Insert
- /// <summary>
- /// IList. insert
- /// </summary>
- /// <param name="index">Index</param>
- /// <param name="value">Value</param>
- private void Insert(int index, T value)
- {
- throw new NotSupportedException();
- }
- #endregion
-
- #region Remove
- /// <summary>
- /// IList. remove
- /// </summary>
- /// <param name="value">Value</param>
- private void Remove(T value)
- {
- throw new NotSupportedException();
- }
- #endregion
-
- #region IndexOf
- /// <summary>
- /// IList. index of
- /// </summary>
- /// <param name="value">Value</param>
- /// <returns>Int</returns>
- private int IndexOf(T value)
- {
- throw new NotSupportedException();
- }
- #endregion
-
- #endregion
- }
-
- /// <summary>
- /// Binary priority queue tests, must be declared outside of a generic class.
- /// </summary>
- public class BinaryPriorityQueueTests
- {
- #region TestBinaryPriorityQueue (Static)
- /// <summary>
- /// Test BinaryPriorityQueue. Note: Too slow for a dynamic unit test.
- /// </summary>
- [Test]
- public static void TestBinaryPriorityQueue()
- {
- BinaryPriorityQueue<int>
- queue = new BinaryPriorityQueue<int>();
-
- // Push entries
- queue.Push(1);
- queue.Push(5);
- queue.Push(9);
- queue.Push(2);
- Assert.Equal(4, queue.Count);
- queue.Push(5);
- Assert.Equal(5, queue.Count);
-
- // Check entries
- Assert.Equal(true, queue.Contains(5));
- Assert.Equal(false, queue.Contains(6));
-
- // Check whole content (list is binary and looks strange ^^)
- Assert.Equal("1, 2, 9, 5, 5",
- ArrayHelper.ToArray(queue).Write());
-
- // And pop everything (should be sorted)
- Assert.Equal(1, queue.Pop());
- Assert.Equal(2, queue.Pop());
- Assert.Equal(5, queue.Pop());
- Assert.Equal(5, queue.Pop());
- Assert.Equal(9, queue.Pop());
- Assert.Equal(0, queue.Count);
- }
- #endregion
- }
- }