tags:

views:

390

answers:

2
+1  Q: 

Heap class in .NET

Possible Duplicate:
Fibonacci, Binary, or Binomial heap in c#?

Is there any class like heap in .NET? I need some kind of collection from which I can retrieve min. element. I just want 3 methods:
-add
-removeMinElement
-getMinElement
I can't use sorted list because there keys has to be unique, and I might have several identical elements.

+2  A: 

You could use SortedList or a SortedDictionary (see discussion below) with a custom key. If you used a type with referential equality, but could be compared based on the value you care about, then this could work.

Something like this:

class HeapKey : IComparable<HeapKey>
{
    public HeapKey(Guid id, Int32 value)
    {
        Id = id;
        Value = value;
    }

    public Guid Id { get; private set; }
    public Int32 Value { get; private set; }

    public int CompareTo(HeapKey other)
    {
        if (_enableCompareCount)
        {
            ++_compareCount;
        }

        if (other == null)
        {
            throw new ArgumentNullException("other");
        }

        var result = Value.CompareTo(other.Value);

        return result == 0 ? Id.CompareTo(other.Id) : result;
    }
}

Here is a working example of using a SortedDictionary which has binary-heap performance characteristics:

using System;
using System.Collections.Generic;
using System.Linq;

namespace SortedDictionaryAsBinaryHeap
{
    class Program
    {
        private static Boolean _enableCompareCount = false;
        private static Int32 _compareCount = 0;

        static void Main(string[] args)
        {
            var rnd = new Random();

            for (int elementCount = 2; elementCount <= 6; elementCount++)
            {
                var keyValues = Enumerable.Range(0, (Int32)Math.Pow(10, elementCount))
                    .Select(i => new HeapKey(Guid.NewGuid(), rnd.Next(0, 10)))
                    .ToDictionary(k => k);
                var heap = new SortedDictionary<HeapKey, HeapKey>(keyValues);

                _compareCount = 0;
                _enableCompareCount = true;
                var min = heap.First().Key;
                _enableCompareCount = false;
                Console.WriteLine("Element count: {0}; Compare count for getMinElement: {1}",
                                  (Int32)Math.Pow(10, elementCount),
                                  _compareCount);   

                _compareCount = 0;
                _enableCompareCount = true;
                heap.Remove(min);
                _enableCompareCount = false;
                Console.WriteLine("Element count: {0}; Compare count for deleteMinElement: {1}", 
                                  (Int32)Math.Pow(10, elementCount),  
                                  _compareCount);   
            }

            Console.ReadKey();
        }

        private class HeapKey : IComparable<HeapKey>
        {
            public HeapKey(Guid id, Int32 value)
            {
                Id = id;
                Value = value;
            }

            public Guid Id { get; private set; }
            public Int32 Value { get; private set; }

            public int CompareTo(HeapKey other)
            {
                if (_enableCompareCount)
                {
                    ++_compareCount;
                }

                if (other == null)
                {
                    throw new ArgumentNullException("other");
                }

                var result = Value.CompareTo(other.Value);

                return result == 0 ? Id.CompareTo(other.Id) : result;
            }
        }
    }
}

Results:

Element count: 100; Compare count for getMinElement: 0

Element count: 100; Compare count for deleteMinElement: 8

Element count: 1000; Compare count for getMinElement: 0

Element count: 1000; Compare count for deleteMinElement: 10

Element count: 10000; Compare count for getMinElement: 0

Element count: 10000; Compare count for deleteMinElement: 13

Element count: 100000; Compare count for getMinElement: 0

Element count: 100000; Compare count for deleteMinElement: 14

Element count: 1000000; Compare count for getMinElement: 0

Element count: 1000000; Compare count for deleteMinElement: 21

codekaizen
SortedList is over-kill, and will have worse performance characteristics.
Matthew Flaschen
@Matthew - how do you know? I would be surprised if it does...
codekaizen
@codek, a SortedList will have O(n) or O(nlog(n)) performance, a PQ will add/remove in O(log(n))
Henk Holterman
@Henk - How is that possible? Both a heap and the SortedList are implemented as binary trees internally - and both have O(log(n)) average case add/remove/find operations. In case of worst-case key collisions (all keys are the same), both will have O(n) performance. Where are you getting these other numbers from?
codekaizen
No a Heap is an implementation != binary (search) tree. See http://en.wikipedia.org/wiki/Priority_queue#Simple_implementations
Henk Holterman
@Henk - read about the heap: http://en.wikipedia.org/wiki/Heap_(data_structure). It is usually implemented as a binary tree. SortedList is the same in .Net. It isn't just sorting a list...
codekaizen
codekaizen, not all binary trees are alike. SortedList has O(n) insertion and removal (http://msdn.microsoft.com/en-us/library/ms132319%28VS.85%29.aspx). A binary heap has worst case O(log n) for insert or deleteMin/Max.
Matthew Flaschen
@Matthew - Ah, right. I confused the SortedList with the SortedDictionary which _does_ have O(log n), and is implemented as a binary heap internally. I've edited the answer accordingly. However, a further point to make is that you are misleading by unilaterally declaring a PQ based on a binary heap structure (even though that is only one implementation) is better in this case. The reason we have different data structures and algorithms satisfies the reality: each have different strengths for different jobs. It may be that for the poster's needs, a list is better.
codekaizen
@Matthew - After thinking more about this, I realize that "delete" only needs to remove 1 of the min elements, since they are identical as regards to the operation. My previous interpretation of needing to search through all min elements to find the one to delete if the keys are the same in the worst case is not applicable. SortedDictionary should have this characteristic.
codekaizen
I didn't mean that a binary heap is better for all applications. What I meant is that a SortedList is wrong when you want heap performance characteristics (as the OP does). I believe SortedDictionary is also wrong here. It is /not/ implemented using a binary heap, nor does it claim to be. Rather, it uses a binary search tree. One of the essential differences is that SortedDictionary can not do getMinElement in constant time.
Matthew Flaschen
@Matthew - I think it can have binary-heap performance. Take a look at the example in the answer.
codekaizen
That solution is good, but I wanted to avoid some extra (unneccesary) data in the node. I thought about another solution, if two nodes are equal just return that first is bigger, but this solution might cause some problems with removing...
Tomek Tarczynski
@Tomek - I tried to use `GetHashCode()` as a substitute for a random, comparable set of bits which I used `Guid` for, but I was getting collisions for some reason. I had thought that the default implementation of `GetHashCode()` returned the object reference handle, and could be relied on to be different between two objects, but apparently I was mistaken, or else implemented it incorrectly.
codekaizen
@codekaizen, you are right that SortedDictionary requires 0 /comparisons/ to implement getMinElement. But it still must /traverse/ log(n) elements (it goes to the left child repeatedly until there is no left child) See TREE-MINIMUN from http://www.personal.kent.edu/~rmuhamma/Algorithms/MyAlgorithms/binarySearchTree.htm . A heap doesn't have to traverse /or/ compare anything for getMinimumElement. It just returns the root element (typically the first item of an internal array).
Matthew Flaschen
@Matthew - well, it really depends on how the SortedDictionary is enumerated internally - it could keep a pointer to the left-most or keep a list of node pointers in sorted order to assist with enumeration. After using Reflector, it appears that none of these are the case - it just does a stack-based traversal of elements, so there is an unnecessary traversal cost if you are just looking for the minimum, although subclassing SortedDictionary and keeping the pointer yourself would be straightfoward.
codekaizen
I don't see how keeping the pointer yourself would be a good solution. When removing the minimum, you would have to call First then to update the pointer. Although that doesn't change the Big-O, it clearly is not true heap performance either.
Matthew Flaschen
A: 

Priority Queues look like a good fit to your problem: http://stackoverflow.com/questions/102398/priority-queue-in-net

Google for "C# priority queues" for more implementations.

ebpower