lemon/radix_heap.h
author Balazs Dezso <deba@google.com>
Fri, 22 Jan 2021 10:55:32 +0100
changeset 1208 c6aa2cc1af04
parent 710 f1fe0ddad6f7
permissions -rw-r--r--
Factor out recursion from weighted matching algorithms (#638)
     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