lemon/binomial_heap.h
author Peter Kovacs <kpeter@inf.elte.hu>
Tue, 15 Mar 2011 19:32:21 +0100
changeset 936 ddd3c0d3d9bf
parent 855 65a0521e744e
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-2010
     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_BINOMIAL_HEAP_H
    20 #define LEMON_BINOMIAL_HEAP_H
    21 
    22 ///\file
    23 ///\ingroup heaps
    24 ///\brief Binomial Heap implementation.
    25 
    26 #include <vector>
    27 #include <utility>
    28 #include <functional>
    29 #include <lemon/math.h>
    30 #include <lemon/counter.h>
    31 
    32 namespace lemon {
    33 
    34   /// \ingroup heaps
    35   ///
    36   ///\brief Binomial heap data structure.
    37   ///
    38   /// This class implements the \e binomial \e heap data structure.
    39   /// It fully conforms to the \ref concepts::Heap "heap concept".
    40   ///
    41   /// The methods \ref increase() and \ref erase() are not efficient
    42   /// in a binomial heap. In case of many calls of these operations,
    43   /// it is better to use other heap structure, e.g. \ref BinHeap
    44   /// "binary heap".
    45   ///
    46   /// \tparam PR Type of the priorities of the items.
    47   /// \tparam IM A read-writable item map with \c int values, used
    48   /// internally to handle the cross references.
    49   /// \tparam CMP A functor class for comparing the priorities.
    50   /// The default is \c std::less<PR>.
    51 #ifdef DOXYGEN
    52   template <typename PR, typename IM, typename CMP>
    53 #else
    54   template <typename PR, typename IM, typename CMP = std::less<PR> >
    55 #endif
    56   class BinomialHeap {
    57   public:
    58     /// Type of the item-int map.
    59     typedef IM ItemIntMap;
    60     /// Type of the priorities.
    61     typedef PR Prio;
    62     /// Type of the items stored in the heap.
    63     typedef typename ItemIntMap::Key Item;
    64     /// Functor type for comparing the priorities.
    65     typedef CMP Compare;
    66 
    67     /// \brief Type to represent the states of the items.
    68     ///
    69     /// Each item has a state associated to it. It can be "in heap",
    70     /// "pre-heap" or "post-heap". The latter two are indifferent from the
    71     /// heap's point of view, but may be useful to the user.
    72     ///
    73     /// The item-int map must be initialized in such way that it assigns
    74     /// \c PRE_HEAP (<tt>-1</tt>) to any element to be put in the heap.
    75     enum State {
    76       IN_HEAP = 0,    ///< = 0.
    77       PRE_HEAP = -1,  ///< = -1.
    78       POST_HEAP = -2  ///< = -2.
    79     };
    80 
    81   private:
    82     class Store;
    83 
    84     std::vector<Store> _data;
    85     int _min, _head;
    86     ItemIntMap &_iim;
    87     Compare _comp;
    88     int _num_items;
    89 
    90   public:
    91     /// \brief Constructor.
    92     ///
    93     /// Constructor.
    94     /// \param map A map that assigns \c int values to the items.
    95     /// It is used internally to handle the cross references.
    96     /// The assigned value must be \c PRE_HEAP (<tt>-1</tt>) for each item.
    97     explicit BinomialHeap(ItemIntMap &map)
    98       : _min(0), _head(-1), _iim(map), _num_items(0) {}
    99 
   100     /// \brief Constructor.
   101     ///
   102     /// Constructor.
   103     /// \param map A map that assigns \c int values to the items.
   104     /// It is used internally to handle the cross references.
   105     /// The assigned value must be \c PRE_HEAP (<tt>-1</tt>) for each item.
   106     /// \param comp The function object used for comparing the priorities.
   107     BinomialHeap(ItemIntMap &map, const Compare &comp)
   108       : _min(0), _head(-1), _iim(map), _comp(comp), _num_items(0) {}
   109 
   110     /// \brief The number of items stored in the heap.
   111     ///
   112     /// This function returns the number of items stored in the heap.
   113     int size() const { return _num_items; }
   114 
   115     /// \brief Check if the heap is empty.
   116     ///
   117     /// This function returns \c true if the heap is empty.
   118     bool empty() const { return _num_items==0; }
   119 
   120     /// \brief Make the heap empty.
   121     ///
   122     /// This functon makes the heap empty.
   123     /// It does not change the cross reference map. If you want to reuse
   124     /// a heap that is not surely empty, you should first clear it and
   125     /// then you should set the cross reference map to \c PRE_HEAP
   126     /// for each item.
   127     void clear() {
   128       _data.clear(); _min=0; _num_items=0; _head=-1;
   129     }
   130 
   131     /// \brief Set the priority of an item or insert it, if it is
   132     /// not stored in the heap.
   133     ///
   134     /// This method sets the priority of the given item if it is
   135     /// already stored in the heap. Otherwise it inserts the given
   136     /// item into the heap with the given priority.
   137     /// \param item The item.
   138     /// \param value The priority.
   139     void set (const Item& item, const Prio& value) {
   140       int i=_iim[item];
   141       if ( i >= 0 && _data[i].in ) {
   142         if ( _comp(value, _data[i].prio) ) decrease(item, value);
   143         if ( _comp(_data[i].prio, value) ) increase(item, value);
   144       } else push(item, value);
   145     }
   146 
   147     /// \brief Insert an item into the heap with the given priority.
   148     ///
   149     /// This function inserts the given item into the heap with the
   150     /// given priority.
   151     /// \param item The item to insert.
   152     /// \param value The priority of the item.
   153     /// \pre \e item must not be stored in the heap.
   154     void push (const Item& item, const Prio& value) {
   155       int i=_iim[item];
   156       if ( i<0 ) {
   157         int s=_data.size();
   158         _iim.set( item,s );
   159         Store st;
   160         st.name=item;
   161         st.prio=value;
   162         _data.push_back(st);
   163         i=s;
   164       }
   165       else {
   166         _data[i].parent=_data[i].right_neighbor=_data[i].child=-1;
   167         _data[i].degree=0;
   168         _data[i].in=true;
   169         _data[i].prio=value;
   170       }
   171 
   172       if( 0==_num_items ) {
   173         _head=i;
   174         _min=i;
   175       } else {
   176         merge(i);
   177         if( _comp(_data[i].prio, _data[_min].prio) ) _min=i;
   178       }
   179       ++_num_items;
   180     }
   181 
   182     /// \brief Return the item having minimum priority.
   183     ///
   184     /// This function returns the item having minimum priority.
   185     /// \pre The heap must be non-empty.
   186     Item top() const { return _data[_min].name; }
   187 
   188     /// \brief The minimum priority.
   189     ///
   190     /// This function returns the minimum priority.
   191     /// \pre The heap must be non-empty.
   192     Prio prio() const { return _data[_min].prio; }
   193 
   194     /// \brief The priority of the given item.
   195     ///
   196     /// This function returns the priority of the given item.
   197     /// \param item The item.
   198     /// \pre \e item must be in the heap.
   199     const Prio& operator[](const Item& item) const {
   200       return _data[_iim[item]].prio;
   201     }
   202 
   203     /// \brief Remove the item having minimum priority.
   204     ///
   205     /// This function removes the item having minimum priority.
   206     /// \pre The heap must be non-empty.
   207     void pop() {
   208       _data[_min].in=false;
   209 
   210       int head_child=-1;
   211       if ( _data[_min].child!=-1 ) {
   212         int child=_data[_min].child;
   213         int neighb;
   214         while( child!=-1 ) {
   215           neighb=_data[child].right_neighbor;
   216           _data[child].parent=-1;
   217           _data[child].right_neighbor=head_child;
   218           head_child=child;
   219           child=neighb;
   220         }
   221       }
   222 
   223       if ( _data[_head].right_neighbor==-1 ) {
   224         // there was only one root
   225         _head=head_child;
   226       }
   227       else {
   228         // there were more roots
   229         if( _head!=_min )  { unlace(_min); }
   230         else { _head=_data[_head].right_neighbor; }
   231         merge(head_child);
   232       }
   233       _min=findMin();
   234       --_num_items;
   235     }
   236 
   237     /// \brief Remove the given item from the heap.
   238     ///
   239     /// This function removes the given item from the heap if it is
   240     /// already stored.
   241     /// \param item The item to delete.
   242     /// \pre \e item must be in the heap.
   243     void erase (const Item& item) {
   244       int i=_iim[item];
   245       if ( i >= 0 && _data[i].in ) {
   246         decrease( item, _data[_min].prio-1 );
   247         pop();
   248       }
   249     }
   250 
   251     /// \brief Decrease the priority of an item to the given value.
   252     ///
   253     /// This function decreases the priority of an item to the given value.
   254     /// \param item The item.
   255     /// \param value The priority.
   256     /// \pre \e item must be stored in the heap with priority at least \e value.
   257     void decrease (Item item, const Prio& value) {
   258       int i=_iim[item];
   259       int p=_data[i].parent;
   260       _data[i].prio=value;
   261 
   262       while( p!=-1 && _comp(value, _data[p].prio) ) {
   263         _data[i].name=_data[p].name;
   264         _data[i].prio=_data[p].prio;
   265         _data[p].name=item;
   266         _data[p].prio=value;
   267         _iim[_data[i].name]=i;
   268         i=p;
   269         p=_data[p].parent;
   270       }
   271       _iim[item]=i;
   272       if ( _comp(value, _data[_min].prio) ) _min=i;
   273     }
   274 
   275     /// \brief Increase the priority of an item to the given value.
   276     ///
   277     /// This function increases the priority of an item to the given value.
   278     /// \param item The item.
   279     /// \param value The priority.
   280     /// \pre \e item must be stored in the heap with priority at most \e value.
   281     void increase (Item item, const Prio& value) {
   282       erase(item);
   283       push(item, value);
   284     }
   285 
   286     /// \brief Return the state of an item.
   287     ///
   288     /// This method returns \c PRE_HEAP if the given item has never
   289     /// been in the heap, \c IN_HEAP if it is in the heap at the moment,
   290     /// and \c POST_HEAP otherwise.
   291     /// In the latter case it is possible that the item will get back
   292     /// to the heap again.
   293     /// \param item The item.
   294     State state(const Item &item) const {
   295       int i=_iim[item];
   296       if( i>=0 ) {
   297         if ( _data[i].in ) i=0;
   298         else i=-2;
   299       }
   300       return State(i);
   301     }
   302 
   303     /// \brief Set the state of an item in the heap.
   304     ///
   305     /// This function sets the state of the given item in the heap.
   306     /// It can be used to manually clear the heap when it is important
   307     /// to achive better time complexity.
   308     /// \param i The item.
   309     /// \param st The state. It should not be \c IN_HEAP.
   310     void state(const Item& i, State st) {
   311       switch (st) {
   312       case POST_HEAP:
   313       case PRE_HEAP:
   314         if (state(i) == IN_HEAP) {
   315           erase(i);
   316         }
   317         _iim[i] = st;
   318         break;
   319       case IN_HEAP:
   320         break;
   321       }
   322     }
   323 
   324   private:
   325 
   326     // Find the minimum of the roots
   327     int findMin() {
   328       if( _head!=-1 ) {
   329         int min_loc=_head, min_val=_data[_head].prio;
   330         for( int x=_data[_head].right_neighbor; x!=-1;
   331              x=_data[x].right_neighbor ) {
   332           if( _comp( _data[x].prio,min_val ) ) {
   333             min_val=_data[x].prio;
   334             min_loc=x;
   335           }
   336         }
   337         return min_loc;
   338       }
   339       else return -1;
   340     }
   341 
   342     // Merge the heap with another heap starting at the given position
   343     void merge(int a) {
   344       if( _head==-1 || a==-1 ) return;
   345       if( _data[a].right_neighbor==-1 &&
   346           _data[a].degree<=_data[_head].degree ) {
   347         _data[a].right_neighbor=_head;
   348         _head=a;
   349       } else {
   350         interleave(a);
   351       }
   352       if( _data[_head].right_neighbor==-1 ) return;
   353 
   354       int x=_head;
   355       int x_prev=-1, x_next=_data[x].right_neighbor;
   356       while( x_next!=-1 ) {
   357         if( _data[x].degree!=_data[x_next].degree ||
   358             ( _data[x_next].right_neighbor!=-1 &&
   359               _data[_data[x_next].right_neighbor].degree==_data[x].degree ) ) {
   360           x_prev=x;
   361           x=x_next;
   362         }
   363         else {
   364           if( _comp(_data[x_next].prio,_data[x].prio) ) {
   365             if( x_prev==-1 ) {
   366               _head=x_next;
   367             } else {
   368               _data[x_prev].right_neighbor=x_next;
   369             }
   370             fuse(x,x_next);
   371             x=x_next;
   372           }
   373           else {
   374             _data[x].right_neighbor=_data[x_next].right_neighbor;
   375             fuse(x_next,x);
   376           }
   377         }
   378         x_next=_data[x].right_neighbor;
   379       }
   380     }
   381 
   382     // Interleave the elements of the given list into the list of the roots
   383     void interleave(int a) {
   384       int p=_head, q=a;
   385       int curr=_data.size();
   386       _data.push_back(Store());
   387 
   388       while( p!=-1 || q!=-1 ) {
   389         if( q==-1 || ( p!=-1 && _data[p].degree<_data[q].degree ) ) {
   390           _data[curr].right_neighbor=p;
   391           curr=p;
   392           p=_data[p].right_neighbor;
   393         }
   394         else {
   395           _data[curr].right_neighbor=q;
   396           curr=q;
   397           q=_data[q].right_neighbor;
   398         }
   399       }
   400 
   401       _head=_data.back().right_neighbor;
   402       _data.pop_back();
   403     }
   404 
   405     // Lace node a under node b
   406     void fuse(int a, int b) {
   407       _data[a].parent=b;
   408       _data[a].right_neighbor=_data[b].child;
   409       _data[b].child=a;
   410 
   411       ++_data[b].degree;
   412     }
   413 
   414     // Unlace node a (if it has siblings)
   415     void unlace(int a) {
   416       int neighb=_data[a].right_neighbor;
   417       int other=_head;
   418 
   419       while( _data[other].right_neighbor!=a )
   420         other=_data[other].right_neighbor;
   421       _data[other].right_neighbor=neighb;
   422     }
   423 
   424   private:
   425 
   426     class Store {
   427       friend class BinomialHeap;
   428 
   429       Item name;
   430       int parent;
   431       int right_neighbor;
   432       int child;
   433       int degree;
   434       bool in;
   435       Prio prio;
   436 
   437       Store() : parent(-1), right_neighbor(-1), child(-1), degree(0),
   438         in(true) {}
   439     };
   440   };
   441 
   442 } //namespace lemon
   443 
   444 #endif //LEMON_BINOMIAL_HEAP_H
   445