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