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