Template class radix_heap<T, traits>

Synopsis

#include "boost/r_heap.hpp"

template <typename T, int b = 32>
class boost::radix_traits
{
  static int const bits = b;
  static unsigned long value(T val);
  static int radix(T val, unsigned long offset);
};

template <typename T, typename traits = boost::radix_traits<T> >
class boost::radix_heap
{
public:
  class iterator
  {
  public:
    iterator();
    
    T const& operator* () const;
    T const* operator->() const;

    iterator& operator++();
    iterator  operator++(int);

    bool operator== (iterator const& it);
    bool operator!= (iterator const& it);
  };

  typedef T        value_type;
  typedef T&       reference;
  typedef T const& const_reference;
  typedef iterator const_iterator;
  typedef int      size_type;
  typedef node*    pointer;

  radix_heap();
  ~radix_heap();

  void      remove(pointer);             // sorry, not yet implemented
  void      change(pointer, T const&);   // sorry, not yet implemented
  void      change_top(T const&);        // sorry, not yet implemented
  void      decrease(pointer, T const&); // sorry, not yet implemented
  void      increase(pointer, T const&); // sorry, not yet implemented

  bool      empty() const;
  size_type size() const;
  pointer   push(T const&);
  T const&  top() const;
  void      pop();

  iterator begin() const;
  iterator end() const;

private:
  radix_heap(radix_heap const&);      // deliberately not implemented
  void operator= (radix_heap const&); // deliberately not implemented
};
    

Note

The current implementation is basically useless because the interesting operations are not implemented at all and some of the implemented operations, notably push() make unrealistic assumptions! In the case of push() it is assumed that no element was removed from the heap making this heap only useful for cases where std::sort() could be used as well (and sorting is much faster). However, I'm sure that the interface can be kept when the code is corrected.

Description

The Radix heap cannot be used for general elements because as part of its approach it is restricted to integral types. Also, it is hard to understand the behavior of this approach when using fast access to the largest element. Although the default behavior of this implementation will provide access to the element with the largest priority, the smallest priority is used for the following description...

Apart from the restriction on integral types, there is another restriction for the Radix heap, namely that elements cannot be decreased beyond the minimum value currently stored in the heap. A priority queue like this is suitable only in very specific cases but eg. Dijkstra's algorithm to find the shortest paths in a graph is one such application. In turn a radix heap seems to be significantly faster (however, I can't really tell because the current implementation has to be changed to implement the missing operations).

The Radix heap maintains a set of buckets and objects with different priority can end up in different buckets. To avoid excessive search for the minimum element, elements are redistributed into unused buckets. Each bucket is assigned a range of elements stored in the bucket where the smaller elements end up in buckets with narrow ranges: Initially, the ranges for the buckets are 0..0, 1..1, 2..3, 4..7, 8..15, and so on. Thus, the correct bucket can be found by determining the radix of the integral value. Now, when elements are removed from the heap, the first buckets become unused and the heap has to search larger buckets for the minimum element. If it has found a bucket with multiple different elements, the heap knows that the smaller buckets are not used. Thus, the elements in the found bucket are redistributed into the unused buckets.

The only description of the Radix heap I have currently available (from Network Flows, Ahuja, Magnanti, Orlin, Prentice Hall) is somewhat silent about the details of the operation. Thus, I haven't implemented some of the operations. Also, it probably does not work to insert an element after there are elements extracted from the heap...

This class does not use a comparator function. Instead, it uses a traits class which is used to determine the radix of the elements. Actually, the traits class has three members:

bits
This is used as constant expression to determine the number of buckets to be used. This is the maximum value which can be returned from radix() plus 1.
value(val)
This function returns an unsigned integral value representing the priority of val. This value is used as an offset passed to the radix() function. This value could in principle be used to determine the radix but I think that it might be possible provide better function determining the radix than the default one...
radix(val, offset)
This function returns the radix of the priority of val minus the offset offset.
The default traits use a static cast to unsigned long to determine the priority of val. To cope with the problem that the other priority queues access the largest element first, the value actually used is (well, should be...) std::numeric_limits<unsigned long>::max() - val.

For a description of the methods of radix_heap<T, traits> see the description of common methods. However, keep in mind that the current implementation does not implement all methods and some methods are probably unstable.

Bugs

Some of the methods are not yet implemented and other methods are probably buggy. However, I have to work on other stuff and need to get the priority queue off my desk. If you really want to use this class or if you have an idea how to implement the missing methods please contact me. Maybe we can work out how to implement the missing stuff. A paper describing Radix heaps would probably also help...

See Also

heap(3), heap-common(3)
Copyright © 1999 Dietmar Kühl (dietmar.kuehl@claas-solutions.de)
Claas Solutions GmbH