-1

I need to increase performance on an algorithm that for every iteration adds up to 4 items to a list and then takes out 1 item with the lowest cost (property on the item). The list is therefor increasing every iteration until the algorithm breaks the loop. Several items can have the same cost and I will just take one of them items in that case.

What data structure should I use?

I am thinking of creating a list where I intend to store the items sorted. Then for every iteration use some kind of sorting algorithm (will research the fastest) and insert the new items at the right position in the list. Then Remove the first item from the list.

user568327
  • 520
  • 4
  • 15
  • 3
    " I am thinking of creating a list where I intend to store the items sorted." - you mean something like... [A SortedList](https://docs.microsoft.com/en-gb/dotnet/api/system.collections.generic.sortedlist-2?view=net-5.0)? – Franz Gleichmann Jan 04 '21 at 20:37
  • I looked at sorted list with the intention to use my cost field as key but doesn't the key need to unique? I can have several items with the same cost value. – user568327 Jan 04 '21 at 20:42
  • 1
    It sounds like what you want is a [priority queue](https://en.wikipedia.org/wiki/Priority_queue). – itsme86 Jan 04 '21 at 20:45
  • 1
    Yes priority queue is what I am looking for. It doesn't look like .net comes with one so I will look into how to implement one myself. Thank you! – user568327 Jan 04 '21 at 20:51
  • You could combine `SortedList` and `Queue` like `SortedList>` where it's sorted on the cost and you queue up the values with the same cost. – juharr Jan 04 '21 at 20:54
  • Check out [this question](https://stackoverflow.com/questions/44221454/bug-in-microsofts-internal-priorityqueuet) if you're using .NET Framework. Looks like there's one in PresentationCore.dll. – itsme86 Jan 04 '21 at 20:54
  • How many items do you expect that will be stored at the list at maximum? – Theodor Zoulias Jan 04 '21 at 21:00
  • @TheodorZoulias, I am not sure at the moment but I am using it within a A* path finding algorithm and I hope to be able to store a lot of data. I playing around with a project in the evenings and are not sure about how big the grids will be where I want to use it. – user568327 Jan 04 '21 at 21:08
  • @juharr, yes I was thinking of that. I will try that solution. – user568327 Jan 04 '21 at 21:09
  • @itsme86, Thanks I will try it out – user568327 Jan 04 '21 at 21:10
  • For a list size less than 100, using a simple `List` should be equally or more performant as any other more sophisticated technique. For larger lists, a possible solution could be to use a `SortedSet>>`, with a custom comparer that ignores the `Value` and takes into account only the `Key`. The [`SortedSet`](https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.sortedset-1) class has a `Min` property. It doesn't allow duplicates, hence the `List` per key. I may post an example if the question is reopened (I've cast a reopen vote). – Theodor Zoulias Jan 04 '21 at 21:41
  • Related: [Priority queue in .Net](https://stackoverflow.com/questions/102398/priority-queue-in-net) – Theodor Zoulias Jan 06 '21 at 18:30

1 Answers1

0

Here is a basic PriorityQueue class that you could use. It is based on a SortedSet<(TSource, TPriority, long)>, with the long having the purpose of differentiating the items with equal priority.

public class PriorityQueue<TSource, TPriority>
{
    private readonly SortedSet<(TSource, TPriority, long)> _set;
    private long _index;

    public PriorityQueue(IComparer<TPriority> priorityComparer = null)
    {
        priorityComparer ??= Comparer<TPriority>.Default;
        var comparer = Comparer<(TSource, TPriority, long)>.Create((x, y) =>
        {
            int result = priorityComparer.Compare(x.Item2, y.Item2);
            if (result == 0) result = Comparer<long>.Default.Compare(x.Item3, y.Item3);
            return result;
        });
        _set = new SortedSet<(TSource, TPriority, long)>(comparer);
    }

    public void Enqueue(TSource item, TPriority priority)
    {
        _set.Add((item, priority, _index++));
    }

    public bool TryDequeue(out TSource item)
    {
        if (_set.Count == 0) { item = default; return false; }
        var min = _set.Min;
        _set.Remove(min);
        item = min.Item1;
        return true;
    }
}

The priority in your case is the cost of each item. Items with lower cost are dequeued first. Smaller TPriority values denote higher priority.

Usage example:

var queue = new PriorityQueue<Item, Decimal>();

//...

queue.Enqueue(item1, item1.Price);
queue.Enqueue(item2, item2.Price);

//...

if (queue.TryDequeue(out var bestPriceItem))
    Console.WriteLine(bestPriceItem);
else
    Console.WriteLine("The warehouse is empty.");

The implementation above should be quite efficient with large numbers of items. The SortedSet<T> is implemented internally as a binary search tree, with the basic operations having a decent O(log n) complexity. But it's not the optimal solution. If you want the best of the best, you could take a look at what may become the official Microsoft's implementation, which is now in the "api-approved" stage (source code). That one is based on a heap instead of a binary search tree, with the same O(log n) complexity, but smaller memory footprint and less constant overhead per operation.

Theodor Zoulias
  • 15,834
  • 3
  • 19
  • 54