lemon/binom_heap.h
author Peter Kovacs <kpeter@inf.elte.hu>
Mon, 20 Jul 2009 19:06:39 +0200
changeset 706 9314d9339475
parent 701 d1a9224f1e30
child 707 3887d6f994d7
permissions -rw-r--r--
Smarter bubbleDown() in K-ary heaps (#301)
     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_BINOM_HEAP_H
    20 #define LEMON_BINOM_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 BinomHeap {
    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 BinomHeap(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     BinomHeap(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         _data.push_back(st);
   162         i=s;
   163       }
   164       else {
   165         _data[i].parent=_data[i].right_neighbor=_data[i].child=-1;
   166         _data[i].degree=0;
   167         _data[i].in=true;
   168       }
   169       _data[i].prio=value;
   170 
   171       if( 0==_num_items ) { _head=i; _min=i; }
   172       else { merge(i); }
   173 
   174       _min = findMin();
   175 
   176       ++_num_items;
   177     }
   178 
   179     /// \brief Return the item having minimum priority.
   180     ///
   181     /// This function returns the item having minimum priority.
   182     /// \pre The heap must be non-empty.
   183     Item top() const { return _data[_min].name; }
   184 
   185     /// \brief The minimum priority.
   186     ///
   187     /// This function returns the minimum priority.
   188     /// \pre The heap must be non-empty.
   189     Prio prio() const { return _data[_min].prio; }
   190 
   191     /// \brief The priority of the given item.
   192     ///
   193     /// This function returns the priority of the given item.
   194     /// \param item The item.
   195     /// \pre \e item must be in the heap.
   196     const Prio& operator[](const Item& item) const {
   197       return _data[_iim[item]].prio;
   198     }
   199 
   200     /// \brief Remove the item having minimum priority.
   201     ///
   202     /// This function removes the item having minimum priority.
   203     /// \pre The heap must be non-empty.
   204     void pop() {
   205       _data[_min].in=false;
   206 
   207       int head_child=-1;
   208       if ( _data[_min].child!=-1 ) {
   209         int child=_data[_min].child;
   210         int neighb;
   211         int prev=-1;
   212         while( child!=-1 ) {
   213           neighb=_data[child].right_neighbor;
   214           _data[child].parent=-1;
   215           _data[child].right_neighbor=prev;
   216           head_child=child;
   217           prev=child;
   218           child=neighb;
   219         }
   220       }
   221 
   222       // The first case is that there are only one root.
   223       if ( -1==_data[_head].right_neighbor ) {
   224         _head=head_child;
   225       }
   226       // The case where there are more roots.
   227       else {
   228         if( _head!=_min )  { unlace(_min); }
   229         else { _head=_data[_head].right_neighbor; }
   230 
   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 
   260       if( _comp( value,_data[i].prio ) ) {
   261         _data[i].prio=value;
   262 
   263         int p_loc=_data[i].parent, loc=i;
   264         int parent, child, neighb;
   265 
   266         while( -1!=p_loc && _comp(_data[loc].prio,_data[p_loc].prio) ) {
   267 
   268           // parent set for other loc_child
   269           child=_data[loc].child;
   270           while( -1!=child ) {
   271             _data[child].parent=p_loc;
   272             child=_data[child].right_neighbor;
   273           }
   274 
   275           // parent set for other p_loc_child
   276           child=_data[p_loc].child;
   277           while( -1!=child ) {
   278             _data[child].parent=loc;
   279             child=_data[child].right_neighbor;
   280           }
   281 
   282           child=_data[p_loc].child;
   283           _data[p_loc].child=_data[loc].child;
   284           if( child==loc )
   285             child=p_loc;
   286           _data[loc].child=child;
   287 
   288           // left_neighb set for p_loc
   289           if( _data[loc].child!=p_loc ) {
   290             while( _data[child].right_neighbor!=loc )
   291               child=_data[child].right_neighbor;
   292             _data[child].right_neighbor=p_loc;
   293           }
   294 
   295           // left_neighb set for loc
   296           parent=_data[p_loc].parent;
   297           if( -1!=parent ) child=_data[parent].child;
   298           else child=_head;
   299 
   300           if( child!=p_loc ) {
   301             while( _data[child].right_neighbor!=p_loc )
   302               child=_data[child].right_neighbor;
   303             _data[child].right_neighbor=loc;
   304           }
   305 
   306           neighb=_data[p_loc].right_neighbor;
   307           _data[p_loc].right_neighbor=_data[loc].right_neighbor;
   308           _data[loc].right_neighbor=neighb;
   309 
   310           _data[p_loc].parent=loc;
   311           _data[loc].parent=parent;
   312 
   313           if( -1!=parent && _data[parent].child==p_loc )
   314             _data[parent].child=loc;
   315 
   316           /*if new parent will be the first root*/
   317           if( _head==p_loc )
   318             _head=loc;
   319 
   320           p_loc=_data[loc].parent;
   321         }
   322       }
   323       if( _comp(value,_data[_min].prio) ) {
   324         _min=i;
   325       }
   326     }
   327 
   328     /// \brief Increase the priority of an item to the given value.
   329     ///
   330     /// This function increases the priority of an item to the given value.
   331     /// \param item The item.
   332     /// \param value The priority.
   333     /// \pre \e item must be stored in the heap with priority at most \e value.
   334     void increase (Item item, const Prio& value) {
   335       erase(item);
   336       push(item, value);
   337     }
   338 
   339     /// \brief Return the state of an item.
   340     ///
   341     /// This method returns \c PRE_HEAP if the given item has never
   342     /// been in the heap, \c IN_HEAP if it is in the heap at the moment,
   343     /// and \c POST_HEAP otherwise.
   344     /// In the latter case it is possible that the item will get back
   345     /// to the heap again.
   346     /// \param item The item.
   347     State state(const Item &item) const {
   348       int i=_iim[item];
   349       if( i>=0 ) {
   350         if ( _data[i].in ) i=0;
   351         else i=-2;
   352       }
   353       return State(i);
   354     }
   355 
   356     /// \brief Set the state of an item in the heap.
   357     ///
   358     /// This function sets the state of the given item in the heap.
   359     /// It can be used to manually clear the heap when it is important
   360     /// to achive better time complexity.
   361     /// \param i The item.
   362     /// \param st The state. It should not be \c IN_HEAP.
   363     void state(const Item& i, State st) {
   364       switch (st) {
   365       case POST_HEAP:
   366       case PRE_HEAP:
   367         if (state(i) == IN_HEAP) {
   368           erase(i);
   369         }
   370         _iim[i] = st;
   371         break;
   372       case IN_HEAP:
   373         break;
   374       }
   375     }
   376 
   377   private:
   378     int findMin() {
   379       int min_loc=-1, min_val;
   380       int x=_head;
   381       if( x!=-1 ) {
   382         min_val=_data[x].prio;
   383         min_loc=x;
   384         x=_data[x].right_neighbor;
   385 
   386         while( x!=-1 ) {
   387           if( _comp( _data[x].prio,min_val ) ) {
   388             min_val=_data[x].prio;
   389             min_loc=x;
   390           }
   391           x=_data[x].right_neighbor;
   392         }
   393       }
   394       return min_loc;
   395     }
   396 
   397     void merge(int a) {
   398       interleave(a);
   399 
   400       int x=_head;
   401       if( -1!=x ) {
   402         int x_prev=-1, x_next=_data[x].right_neighbor;
   403         while( -1!=x_next ) {
   404           if( _data[x].degree!=_data[x_next].degree || ( -1!=_data[x_next].right_neighbor && _data[_data[x_next].right_neighbor].degree==_data[x].degree ) ) {
   405             x_prev=x;
   406             x=x_next;
   407           }
   408           else {
   409             if( _comp(_data[x].prio,_data[x_next].prio) ) {
   410               _data[x].right_neighbor=_data[x_next].right_neighbor;
   411               fuse(x_next,x);
   412             }
   413             else {
   414               if( -1==x_prev ) { _head=x_next; }
   415               else {
   416                 _data[x_prev].right_neighbor=x_next;
   417               }
   418               fuse(x,x_next);
   419               x=x_next;
   420             }
   421           }
   422           x_next=_data[x].right_neighbor;
   423         }
   424       }
   425     }
   426 
   427     void interleave(int a) {
   428       int other=-1, head_other=-1;
   429 
   430       while( -1!=a || -1!=_head ) {
   431         if( -1==a ) {
   432           if( -1==head_other ) {
   433             head_other=_head;
   434           }
   435           else {
   436             _data[other].right_neighbor=_head;
   437           }
   438           _head=-1;
   439         }
   440         else if( -1==_head ) {
   441           if( -1==head_other ) {
   442             head_other=a;
   443           }
   444           else {
   445             _data[other].right_neighbor=a;
   446           }
   447           a=-1;
   448         }
   449         else {
   450           if( _data[a].degree<_data[_head].degree ) {
   451             if( -1==head_other ) {
   452               head_other=a;
   453             }
   454             else {
   455               _data[other].right_neighbor=a;
   456             }
   457             other=a;
   458             a=_data[a].right_neighbor;
   459           }
   460           else {
   461             if( -1==head_other ) {
   462               head_other=_head;
   463             }
   464             else {
   465               _data[other].right_neighbor=_head;
   466             }
   467             other=_head;
   468             _head=_data[_head].right_neighbor;
   469           }
   470         }
   471       }
   472       _head=head_other;
   473     }
   474 
   475     // Lacing a under b
   476     void fuse(int a, int b) {
   477       _data[a].parent=b;
   478       _data[a].right_neighbor=_data[b].child;
   479       _data[b].child=a;
   480 
   481       ++_data[b].degree;
   482     }
   483 
   484     // It is invoked only if a has siblings.
   485     void unlace(int a) {
   486       int neighb=_data[a].right_neighbor;
   487       int other=_head;
   488 
   489       while( _data[other].right_neighbor!=a )
   490         other=_data[other].right_neighbor;
   491       _data[other].right_neighbor=neighb;
   492     }
   493 
   494   private:
   495 
   496     class store {
   497       friend class BinomHeap;
   498 
   499       Item name;
   500       int parent;
   501       int right_neighbor;
   502       int child;
   503       int degree;
   504       bool in;
   505       Prio prio;
   506 
   507       store() : parent(-1), right_neighbor(-1), child(-1), degree(0), in(true) {}
   508     };
   509   };
   510 
   511 } //namespace lemon
   512 
   513 #endif //LEMON_BINOM_HEAP_H
   514