#region License /* Copyright (c) 2006 Leslie Sanford * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #endregion #region Contact /* * Leslie Sanford * Email: jabberdabber@hotmail.com */ #endregion using System; using System.Collections; using System.Diagnostics; namespace LSCollections { /// /// Represents the priority queue data structure. /// public class PriorityQueue : ICollection { #region PriorityQueue Members #region Fields // The maximum level of the skip list. private const int LevelMaxValue = 16; // The probability value used to randomly select the next level value. private const double Probability = 0.5; // The current level of the skip list. private int currentLevel = 1; // The header node of the skip list. private Node header = new Node(null, LevelMaxValue); // Used to generate node levels. private Random rand = new Random(); // The number of elements in the PriorityQueue. private int count = 0; // The version of this PriorityQueue. private long version = 0; // Used for comparing and sorting elements. private IComparer comparer; #endregion #region Construction /// /// Initializes a new instance of the PriorityQueue class. /// /// /// The PriorityQueue will cast its elements to the IComparable /// interface when making comparisons. /// public PriorityQueue() { comparer = new DefaultComparer(); } /// /// Initializes a new instance of the PriorityQueue class with the /// specified IComparer. /// /// /// The IComparer to use for comparing and ordering elements. /// /// /// If the specified IComparer is null, the PriorityQueue will cast its /// elements to the IComparable interface when making comparisons. /// public PriorityQueue(IComparer comparer) { // If no comparer was provided. if(comparer == null) { // Use the DefaultComparer. this.comparer = new DefaultComparer(); } // Else a comparer was provided. else { // Use the provided comparer. this.comparer = comparer; } } #endregion #region Methods /// /// Enqueues the specified element into the PriorityQueue. /// /// /// The element to enqueue into the PriorityQueue. /// /// /// If element is null. /// public virtual void Enqueue(object element) { #region Require if(element == null) { throw new ArgumentNullException("element"); } #endregion Node x = header; Node[] update = new Node[LevelMaxValue]; int nextLevel = NextLevel(); // Find the place in the queue to insert the new element. for(int i = currentLevel - 1; i >= 0; i--) { while(x[i] != null && comparer.Compare(x[i].Element, element) > 0) { x = x[i]; } update[i] = x; } // If the new node's level is greater than the current level. if(nextLevel > currentLevel) { for(int i = currentLevel; i < nextLevel; i++) { update[i] = header; } // Update level. currentLevel = nextLevel; } // Create new node. Node newNode = new Node(element, nextLevel); // Insert the new node into the list. for(int i = 0; i < nextLevel; i++) { newNode[i] = update[i][i]; update[i][i] = newNode; } // Keep track of the number of elements in the PriorityQueue. count++; version++; #region Ensure Debug.Assert(Contains(element), "Contains Test", "Contains test for element " + element.ToString() + " failed."); #endregion #region Invariant AssertValid(); #endregion } /// /// Removes the element at the head of the PriorityQueue. /// /// /// The element at the head of the PriorityQueue. /// /// /// If Count is zero. /// public virtual object Dequeue() { #region Require if(Count == 0) { throw new InvalidOperationException( "Cannot dequeue into an empty PriorityQueue."); } #endregion // Get the first item in the queue. object element = header[0].Element; // Keep track of the node that is about to be removed. Node oldNode = header[0]; // Update the header so that its pointers that pointed to the // node to be removed now point to the node that comes after it. for(int i = 0; i < currentLevel && header[i] == oldNode; i++) { header[i] = oldNode[i]; } // Update the current level of the list in case the node that // was removed had the highest level. while(currentLevel > 1 && header[currentLevel - 1] == null) { currentLevel--; } // Keep track of how many items are in the queue. count--; version++; #region Ensure Debug.Assert(count >= 0); #endregion #region Invariant AssertValid(); #endregion return element; } /// /// Removes the specified element from the PriorityQueue. /// /// /// The element to remove. /// /// /// If element is null /// public virtual void Remove(object element) { #region Require if(element == null) { throw new ArgumentNullException("element"); } #endregion Node x = header; Node[] update = new Node[LevelMaxValue]; int nextLevel = NextLevel(); // Find the specified element. for(int i = currentLevel - 1; i >= 0; i--) { while(x[i] != null && comparer.Compare(x[i].Element, element) > 0) { x = x[i]; } update[i] = x; } x = x[0]; // If the specified element was found. if(x != null && comparer.Compare(x.Element, element) == 0) { // Remove element. for(int i = 0; i < currentLevel && update[i][i] == x; i++) { update[i][i] = x[i]; } // Update list level. while(currentLevel > 1 && header[currentLevel - 1] == null) { currentLevel--; } // Keep track of the number of elements in the PriorityQueue. count--; version++; } #region Invariant AssertValid(); #endregion } /// /// Returns a value indicating whether the specified element is in the /// PriorityQueue. /// /// /// The element to test. /// /// /// true if the element is in the PriorityQueue; otherwise /// false. /// public virtual bool Contains(object element) { #region Guard if(element == null) { return false; } #endregion bool found; Node x = header; // Find the specified element. for(int i = currentLevel - 1; i >= 0; i--) { while(x[i] != null && comparer.Compare(x[i].Element, element) > 0) { x = x[i]; } } x = x[0]; // If the element is in the PriorityQueue. if(x != null && comparer.Compare(x.Element, element) == 0) { found = true; } // Else the element is not in the PriorityQueue. else { found = false; } return found; } /// /// Returns the element at the head of the PriorityQueue without /// removing it. /// /// /// The element at the head of the PriorityQueue. /// public virtual object Peek() { #region Require if(Count == 0) { throw new InvalidOperationException( "Cannot peek into an empty PriorityQueue."); } #endregion return header[0].Element; } /// /// Removes all elements from the PriorityQueue. /// public virtual void Clear() { header = new Node(null, LevelMaxValue); currentLevel = 1; count = 0; version++; #region Invariant AssertValid(); #endregion } /// /// Returns a synchronized wrapper of the specified PriorityQueue. /// /// /// The PriorityQueue to synchronize. /// /// /// A synchronized PriorityQueue. /// /// /// If queue is null. /// public static PriorityQueue Synchronized(PriorityQueue queue) { #region Require if(queue == null) { throw new ArgumentNullException("queue"); } #endregion return new SynchronizedPriorityQueue(queue); } // Generates a random level for the next node. private int NextLevel() { int nextLevel = 1; while(rand.NextDouble() < Probability && nextLevel < LevelMaxValue && nextLevel <= currentLevel) { nextLevel++; } return nextLevel; } // Makes sure none of the PriorityQueue's invariants have been violated. [Conditional("DEBUG")] private void AssertValid() { int n = 0; Node x = header[0]; while(x != null) { if(x[0] != null) { Debug.Assert(comparer.Compare(x.Element, x[0].Element) >= 0, "Order test"); } x = x[0]; n++; } Debug.Assert(n == Count, "Count test."); for(int i = 1; i < currentLevel; i++) { Debug.Assert(header[i] != null, "Level non-null test."); } for(int i = currentLevel; i < LevelMaxValue; i++) { Debug.Assert(header[i] == null, "Level null test."); } } [Conditional("DEBUG")] public static void Test() { Random r = new Random(); PriorityQueue queue = new PriorityQueue(); int count = 1000; int element; for(int i = 0; i < count; i++) { element = r.Next(); queue.Enqueue(element); Debug.Assert(queue.Contains(element), "Contains Test"); } Debug.Assert(queue.Count == count, "Count Test"); int previousElement = (int)queue.Peek(); int peekElement; for(int i = 0; i < count; i++) { peekElement = (int)queue.Peek(); element = (int)queue.Dequeue(); Debug.Assert(element == peekElement, "Peek Test"); Debug.Assert(element <= previousElement, "Order Test"); previousElement = element; } Debug.Assert(queue.Count == 0); } #endregion #region Private Classes #region SynchronizedPriorityQueue Class // A synchronized wrapper for the PriorityQueue class. private class SynchronizedPriorityQueue : PriorityQueue { private PriorityQueue queue; private object root; public SynchronizedPriorityQueue(PriorityQueue queue) { #region Require if(queue == null) { throw new ArgumentNullException("queue"); } #endregion this.queue = queue; root = queue.SyncRoot; } public override void Enqueue(object element) { lock(root) { queue.Enqueue(element); } } public override object Dequeue() { lock(root) { return queue.Dequeue(); } } public override void Remove(object element) { lock(root) { queue.Remove(element); } } public override void Clear() { lock(root) { queue.Clear(); } } public override bool Contains(object element) { lock(root) { return queue.Contains(element); } } public override object Peek() { lock(root) { return queue.Peek(); } } public override void CopyTo(Array array, int index) { lock(root) { queue.CopyTo(array, index); } } public override int Count { get { lock(root) { return queue.Count; } } } public override bool IsSynchronized { get { return true; } } public override object SyncRoot { get { return root; } } public override IEnumerator GetEnumerator() { lock(root) { return queue.GetEnumerator(); } } } #endregion #region DefaultComparer Class // The IComparer to use of no comparer was provided. private class DefaultComparer : IComparer { #region IComparer Members public int Compare(object x, object y) { #region Require if(!(y is IComparable)) { throw new ArgumentException( "Item does not implement IComparable."); } #endregion IComparable a = x as IComparable; Debug.Assert(a != null); return a.CompareTo(y); } #endregion } #endregion #region Node Class // Represents a node in the list of nodes. private class Node { private Node[] forward; private object element; public Node(object element, int level) { this.forward = new Node[level]; this.element = element; } public Node this[int index] { get { return forward[index]; } set { forward[index] = value; } } public object Element { get { return element; } } } #endregion #region PriorityQueueEnumerator Class // Implements the IEnumerator interface for the PriorityQueue class. private class PriorityQueueEnumerator : IEnumerator { private PriorityQueue owner; private Node head; private Node currentNode; private bool moveResult; private long version; public PriorityQueueEnumerator(PriorityQueue owner) { this.owner = owner; this.version = owner.version; head = owner.header; Reset(); } #region IEnumerator Members public void Reset() { #region Require if(version != owner.version) { throw new InvalidOperationException( "The PriorityQueue was modified after the enumerator was created."); } #endregion currentNode = head; moveResult = true; } public object Current { get { #region Require if(currentNode == head || currentNode == null) { throw new InvalidOperationException( "The enumerator is positioned before the first " + "element of the collection or after the last element."); } #endregion return currentNode.Element; } } public bool MoveNext() { #region Require if(version != owner.version) { throw new InvalidOperationException( "The PriorityQueue was modified after the enumerator was created."); } #endregion if(moveResult) { currentNode = currentNode[0]; } if(currentNode == null) { moveResult = false; } return moveResult; } #endregion } #endregion #endregion #endregion #region ICollection Members public virtual bool IsSynchronized { get { return false; } } public virtual int Count { get { return count; } } public virtual void CopyTo(Array array, int index) { #region Require if(array == null) { throw new ArgumentNullException("array"); } else if(index < 0) { throw new ArgumentOutOfRangeException("index", index, "Array index out of range."); } else if(array.Rank > 1) { throw new ArgumentException( "Array has more than one dimension.", "array"); } else if(index >= array.Length) { throw new ArgumentException( "index is equal to or greater than the length of array.", "index"); } else if(Count > array.Length - index) { throw new ArgumentException( "The number of elements in the PriorityQueue is greater " + "than the available space from index to the end of the " + "destination array.", "index"); } #endregion int i = index; foreach(object element in this) { array.SetValue(element, i); i++; } } public virtual object SyncRoot { get { return this; } } #endregion #region IEnumerable Members public virtual IEnumerator GetEnumerator() { return new PriorityQueueEnumerator(this); } #endregion } }