I would like to implement a priority queue which would inject my objects - Nodes
to the queue with respect to one field - f
. I have already written List with custom comparer but this would require from me to:
My List should be always sorted according to some field (here I need it to be sorted by f
). I also need the ability to add and dequeue
the object with lowest value from the list.
Can someone tell me what would be the best approach to this ?
EDIT
dasblinkenlight has a very good answer but just I have realised that I should be able to store duplicates in this container.
If you are in .NET 4 or later, you can use a SortedSet<T>
class with a custom IComparer<T>
.
The class lets you add new objects with the Add
method common to all mutable collections. You can retrieve the max element using the Max
property, and then call Remove
to erase the max from the collection.
Edit: (in response to an edit of the question) If you need to store duplicates, you can use a SortedDictionary<Key,int>
, and make a counted set out of it. Again, you have an option of using a custom IComparer<T>
. When you enqueue an element, check if it exists already, and increment its count. When dequeueing, again check the count, decrement it, and remove the key only when the count reaches zero.
As mentioned earlier, a SortedDictionary
gets you part way there, you just need a way of dealing with duplicate priorities. The way of dealing with duplicates in any Map based structure (Dictionary
, SortedDictionary
, etc.) is to make the value a collection of what you really want the values to be. In your case, it makes the most sense for the value to be a Queue<T>
.
Here is a starting place. You can add additional methods such as Count
, implementations of IEnumerable
, etc. if needed.
/// <summary>
///
/// </summary>
/// <typeparam name="TElement">The type of the actual elements that are stored</typeparam>
/// <typeparam name="TKey">The type of the priority. It probably makes sense to be an int or long, \
/// but any type that can be the key of a SortedDictionary will do.</typeparam>
public class PriorityQueue<TElement, TKey>
{
private SortedDictionary<TKey, Queue<TElement>> dictionary = new SortedDictionary<TKey, Queue<TElement>>();
private Func<TElement, TKey> selector;
public PriorityQueue(Func<TElement, TKey> selector)
{
this.selector = selector;
}
public void Enqueue(TElement item)
{
TKey key = selector(item);
Queue<TElement> queue;
if (!dictionary.TryGetValue(key, out queue))
{
queue = new Queue<TElement>();
dictionary.Add(key, queue);
}
queue.Enqueue(item);
}
public TElement Dequeue()
{
if (dictionary.Count == 0)
throw new Exception("No items to Dequeue:");
var key = dictionary.Keys.First();
var queue = dictionary[key];
var output = queue.Dequeue();
if (queue.Count == 0)
dictionary.Remove(key);
return output;
}
}