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