lemon/radix_heap.h
author Peter Kovacs <kpeter@inf.elte.hu>
Tue, 15 Mar 2011 19:32:21 +0100
changeset 936 ddd3c0d3d9bf
parent 710 f1fe0ddad6f7
permissions -rw-r--r--
Implement the scaling Price Refinement heuristic in CostScaling (#417)
instead of Early Termination.

These two heuristics are similar, but the newer one is faster
and not only makes it possible to skip some epsilon phases, but
it can improve the performance of the other phases, as well.
     1 /* -*- mode: C++; indent-tabs-mode: nil; -*-
     2  *
     3  * This file is a part of LEMON, a generic C++ optimization library.
     4  *
     5  * Copyright (C) 2003-2009
     6  * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport
     7  * (Egervary Research Group on Combinatorial Optimization, EGRES).
     8  *
     9  * Permission to use, modify and distribute this software is granted
    10  * provided that this copyright notice appears in all copies. For
    11  * precise terms see the accompanying LICENSE file.
    12  *
    13  * This software is provided "AS IS" with no warranty of any kind,
    14  * express or implied, and with no claim as to its suitability for any
    15  * purpose.
    16  *
    17  */
    18 
    19 #ifndef LEMON_RADIX_HEAP_H
    20 #define LEMON_RADIX_HEAP_H
    21 
    22 ///\ingroup heaps
    23 ///\file
    24 ///\brief Radix heap implementation.
    25 
    26 #include <vector>
    27 #include <lemon/error.h>
    28 
    29 namespace lemon {
    30 
    31 
    32   /// \ingroup heaps
    33   ///
    34   /// \brief Radix heap data structure.
    35   ///
    36   /// This class implements the \e radix \e heap data structure.
    37   /// It practically conforms to the \ref concepts::Heap "heap concept",
    38   /// but it has some limitations due its special implementation.
    39   /// The type of the priorities must be \c int and the priority of an
    40   /// item cannot be decreased under the priority of the last removed item.
    41   ///
    42   /// \tparam IM A read-writable item map with \c int values, used
    43   /// internally to handle the cross references.
    44   template <typename IM>
    45   class RadixHeap {
    46 
    47   public:
    48 
    49     /// Type of the item-int map.
    50     typedef IM ItemIntMap;
    51     /// Type of the priorities.
    52     typedef int Prio;
    53     /// Type of the items stored in the heap.
    54     typedef typename ItemIntMap::Key Item;
    55 
    56     /// \brief Exception thrown by RadixHeap.
    57     ///
    58     /// This exception is thrown when an item is inserted into a
    59     /// RadixHeap with a priority smaller than the last erased one.
    60     /// \see RadixHeap
    61     class PriorityUnderflowError : public Exception {
    62     public:
    63       virtual const char* what() const throw() {
    64         return "lemon::RadixHeap::PriorityUnderflowError";
    65       }
    66     };
    67 
    68     /// \brief Type to represent the states of the items.
    69     ///
    70     /// Each item has a state associated to it. It can be "in heap",
    71     /// "pre-heap" or "post-heap". The latter two are indifferent from the
    72     /// heap's point of view, but may be useful to the user.
    73     ///
    74     /// The item-int map must be initialized in such way that it assigns
    75     /// \c PRE_HEAP (<tt>-1</tt>) to any element to be put in the heap.
    76     enum State {
    77       IN_HEAP = 0,    ///< = 0.
    78       PRE_HEAP = -1,  ///< = -1.
    79       POST_HEAP = -2  ///< = -2.
    80     };
    81 
    82   private:
    83 
    84     struct RadixItem {
    85       int prev, next, box;
    86       Item item;
    87       int prio;
    88       RadixItem(Item _item, int _prio) : item(_item), prio(_prio) {}
    89     };
    90 
    91     struct RadixBox {
    92       int first;
    93       int min, size;
    94       RadixBox(int _min, int _size) : first(-1), min(_min), size(_size) {}
    95     };
    96 
    97     std::vector<RadixItem> _data;
    98     std::vector<RadixBox> _boxes;
    99 
   100     ItemIntMap &_iim;
   101 
   102   public:
   103 
   104     /// \brief Constructor.
   105     ///
   106     /// Constructor.
   107     /// \param map A map that assigns \c int values to the items.
   108     /// It is used internally to handle the cross references.
   109     /// The assigned value must be \c PRE_HEAP (<tt>-1</tt>) for each item.
   110     /// \param minimum The initial minimum value of the heap.
   111     /// \param capacity The initial capacity of the heap.
   112     RadixHeap(ItemIntMap &map, int minimum = 0, int capacity = 0)
   113       : _iim(map)
   114     {
   115       _boxes.push_back(RadixBox(minimum, 1));
   116       _boxes.push_back(RadixBox(minimum + 1, 1));
   117       while (lower(_boxes.size() - 1, capacity + minimum - 1)) {
   118         extend();
   119       }
   120     }
   121 
   122     /// \brief The number of items stored in the heap.
   123     ///
   124     /// This function returns the number of items stored in the heap.
   125     int size() const { return _data.size(); }
   126 
   127     /// \brief Check if the heap is empty.
   128     ///
   129     /// This function returns \c true if the heap is empty.
   130     bool empty() const { return _data.empty(); }
   131 
   132     /// \brief Make the heap empty.
   133     ///
   134     /// This functon makes the heap empty.
   135     /// It does not change the cross reference map. If you want to reuse
   136     /// a heap that is not surely empty, you should first clear it and
   137     /// then you should set the cross reference map to \c PRE_HEAP
   138     /// for each item.
   139     /// \param minimum The minimum value of the heap.
   140     /// \param capacity The capacity of the heap.
   141     void clear(int minimum = 0, int capacity = 0) {
   142       _data.clear(); _boxes.clear();
   143       _boxes.push_back(RadixBox(minimum, 1));
   144       _boxes.push_back(RadixBox(minimum + 1, 1));
   145       while (lower(_boxes.size() - 1, capacity + minimum - 1)) {
   146         extend();
   147       }
   148     }
   149 
   150   private:
   151 
   152     bool upper(int box, Prio pr) {
   153       return pr < _boxes[box].min;
   154     }
   155 
   156     bool lower(int box, Prio pr) {
   157       return pr >= _boxes[box].min + _boxes[box].size;
   158     }
   159 
   160     // Remove item from the box list
   161     void remove(int index) {
   162       if (_data[index].prev >= 0) {
   163         _data[_data[index].prev].next = _data[index].next;
   164       } else {
   165         _boxes[_data[index].box].first = _data[index].next;
   166       }
   167       if (_data[index].next >= 0) {
   168         _data[_data[index].next].prev = _data[index].prev;
   169       }
   170     }
   171 
   172     // Insert item into the box list
   173     void insert(int box, int index) {
   174       if (_boxes[box].first == -1) {
   175         _boxes[box].first = index;
   176         _data[index].next = _data[index].prev = -1;
   177       } else {
   178         _data[index].next = _boxes[box].first;
   179         _data[_boxes[box].first].prev = index;
   180         _data[index].prev = -1;
   181         _boxes[box].first = index;
   182       }
   183       _data[index].box = box;
   184     }
   185 
   186     // Add a new box to the box list
   187     void extend() {
   188       int min = _boxes.back().min + _boxes.back().size;
   189       int bs = 2 * _boxes.back().size;
   190       _boxes.push_back(RadixBox(min, bs));
   191     }
   192 
   193     // Move an item up into the proper box.
   194     void bubbleUp(int index) {
   195       if (!lower(_data[index].box, _data[index].prio)) return;
   196       remove(index);
   197       int box = findUp(_data[index].box, _data[index].prio);
   198       insert(box, index);
   199     }
   200 
   201     // Find up the proper box for the item with the given priority
   202     int findUp(int start, int pr) {
   203       while (lower(start, pr)) {
   204         if (++start == int(_boxes.size())) {
   205           extend();
   206         }
   207       }
   208       return start;
   209     }
   210 
   211     // Move an item down into the proper box
   212     void bubbleDown(int index) {
   213       if (!upper(_data[index].box, _data[index].prio)) return;
   214       remove(index);
   215       int box = findDown(_data[index].box, _data[index].prio);
   216       insert(box, index);
   217     }
   218 
   219     // Find down the proper box for the item with the given priority
   220     int findDown(int start, int pr) {
   221       while (upper(start, pr)) {
   222         if (--start < 0) throw PriorityUnderflowError();
   223       }
   224       return start;
   225     }
   226 
   227     // Find the first non-empty box
   228     int findFirst() {
   229       int first = 0;
   230       while (_boxes[first].first == -1) ++first;
   231       return first;
   232     }
   233 
   234     // Gives back the minimum priority of the given box
   235     int minValue(int box) {
   236       int min = _data[_boxes[box].first].prio;
   237       for (int k = _boxes[box].first; k != -1; k = _data[k].next) {
   238         if (_data[k].prio < min) min = _data[k].prio;
   239       }
   240       return min;
   241     }
   242 
   243     // Rearrange the items of the heap and make the first box non-empty
   244     void moveDown() {
   245       int box = findFirst();
   246       if (box == 0) return;
   247       int min = minValue(box);
   248       for (int i = 0; i <= box; ++i) {
   249         _boxes[i].min = min;
   250         min += _boxes[i].size;
   251       }
   252       int curr = _boxes[box].first, next;
   253       while (curr != -1) {
   254         next = _data[curr].next;
   255         bubbleDown(curr);
   256         curr = next;
   257       }
   258     }
   259 
   260     void relocateLast(int index) {
   261       if (index != int(_data.size()) - 1) {
   262         _data[index] = _data.back();
   263         if (_data[index].prev != -1) {
   264           _data[_data[index].prev].next = index;
   265         } else {
   266           _boxes[_data[index].box].first = index;
   267         }
   268         if (_data[index].next != -1) {
   269           _data[_data[index].next].prev = index;
   270         }
   271         _iim[_data[index].item] = index;
   272       }
   273       _data.pop_back();
   274     }
   275 
   276   public:
   277 
   278     /// \brief Insert an item into the heap with the given priority.
   279     ///
   280     /// This function inserts the given item into the heap with the
   281     /// given priority.
   282     /// \param i The item to insert.
   283     /// \param p The priority of the item.
   284     /// \pre \e i must not be stored in the heap.
   285     /// \warning This method may throw an \c UnderFlowPriorityException.
   286     void push(const Item &i, const Prio &p) {
   287       int n = _data.size();
   288       _iim.set(i, n);
   289       _data.push_back(RadixItem(i, p));
   290       while (lower(_boxes.size() - 1, p)) {
   291         extend();
   292       }
   293       int box = findDown(_boxes.size() - 1, p);
   294       insert(box, n);
   295     }
   296 
   297     /// \brief Return the item having minimum priority.
   298     ///
   299     /// This function returns the item having minimum priority.
   300     /// \pre The heap must be non-empty.
   301     Item top() const {
   302       const_cast<RadixHeap<ItemIntMap>&>(*this).moveDown();
   303       return _data[_boxes[0].first].item;
   304     }
   305 
   306     /// \brief The minimum priority.
   307     ///
   308     /// This function returns the minimum priority.
   309     /// \pre The heap must be non-empty.
   310     Prio prio() const {
   311       const_cast<RadixHeap<ItemIntMap>&>(*this).moveDown();
   312       return _data[_boxes[0].first].prio;
   313      }
   314 
   315     /// \brief Remove the item having minimum priority.
   316     ///
   317     /// This function removes the item having minimum priority.
   318     /// \pre The heap must be non-empty.
   319     void pop() {
   320       moveDown();
   321       int index = _boxes[0].first;
   322       _iim[_data[index].item] = POST_HEAP;
   323       remove(index);
   324       relocateLast(index);
   325     }
   326 
   327     /// \brief Remove the given item from the heap.
   328     ///
   329     /// This function removes the given item from the heap if it is
   330     /// already stored.
   331     /// \param i The item to delete.
   332     /// \pre \e i must be in the heap.
   333     void erase(const Item &i) {
   334       int index = _iim[i];
   335       _iim[i] = POST_HEAP;
   336       remove(index);
   337       relocateLast(index);
   338    }
   339 
   340     /// \brief The priority of the given item.
   341     ///
   342     /// This function returns the priority of the given item.
   343     /// \param i The item.
   344     /// \pre \e i must be in the heap.
   345     Prio operator[](const Item &i) const {
   346       int idx = _iim[i];
   347       return _data[idx].prio;
   348     }
   349 
   350     /// \brief Set the priority of an item or insert it, if it is
   351     /// not stored in the heap.
   352     ///
   353     /// This method sets the priority of the given item if it is
   354     /// already stored in the heap. Otherwise it inserts the given
   355     /// item into the heap with the given priority.
   356     /// \param i The item.
   357     /// \param p The priority.
   358     /// \pre \e i must be in the heap.
   359     /// \warning This method may throw an \c UnderFlowPriorityException.
   360     void set(const Item &i, const Prio &p) {
   361       int idx = _iim[i];
   362       if( idx < 0 ) {
   363         push(i, p);
   364       }
   365       else if( p >= _data[idx].prio ) {
   366         _data[idx].prio = p;
   367         bubbleUp(idx);
   368       } else {
   369         _data[idx].prio = p;
   370         bubbleDown(idx);
   371       }
   372     }
   373 
   374     /// \brief Decrease the priority of an item to the given value.
   375     ///
   376     /// This function decreases the priority of an item to the given value.
   377     /// \param i The item.
   378     /// \param p The priority.
   379     /// \pre \e i must be stored in the heap with priority at least \e p.
   380     /// \warning This method may throw an \c UnderFlowPriorityException.
   381     void decrease(const Item &i, const Prio &p) {
   382       int idx = _iim[i];
   383       _data[idx].prio = p;
   384       bubbleDown(idx);
   385     }
   386 
   387     /// \brief Increase the priority of an item to the given value.
   388     ///
   389     /// This function increases the priority of an item to the given value.
   390     /// \param i The item.
   391     /// \param p The priority.
   392     /// \pre \e i must be stored in the heap with priority at most \e p.
   393     void increase(const Item &i, const Prio &p) {
   394       int idx = _iim[i];
   395       _data[idx].prio = p;
   396       bubbleUp(idx);
   397     }
   398 
   399     /// \brief Return the state of an item.
   400     ///
   401     /// This method returns \c PRE_HEAP if the given item has never
   402     /// been in the heap, \c IN_HEAP if it is in the heap at the moment,
   403     /// and \c POST_HEAP otherwise.
   404     /// In the latter case it is possible that the item will get back
   405     /// to the heap again.
   406     /// \param i The item.
   407     State state(const Item &i) const {
   408       int s = _iim[i];
   409       if( s >= 0 ) s = 0;
   410       return State(s);
   411     }
   412 
   413     /// \brief Set the state of an item in the heap.
   414     ///
   415     /// This function sets the state of the given item in the heap.
   416     /// It can be used to manually clear the heap when it is important
   417     /// to achive better time complexity.
   418     /// \param i The item.
   419     /// \param st The state. It should not be \c IN_HEAP.
   420     void state(const Item& i, State st) {
   421       switch (st) {
   422       case POST_HEAP:
   423       case PRE_HEAP:
   424         if (state(i) == IN_HEAP) {
   425           erase(i);
   426         }
   427         _iim[i] = st;
   428         break;
   429       case IN_HEAP:
   430         break;
   431       }
   432     }
   433 
   434   }; // class RadixHeap
   435 
   436 } // namespace lemon
   437 
   438 #endif // LEMON_RADIX_HEAP_H