COIN-OR::LEMON - Graph Library

source: lemon-0.x/lemon/radix_heap.h @ 2288:ef8af928c54e

Last change on this file since 2288:ef8af928c54e was 2263:9273fe7d850c, checked in by mqrelly, 17 years ago

Bug #46 fixed: Superfluous template parameter in Heap concept
NOTE: Not every affected file tested.

File size: 12.3 KB
Line 
1/* -*- C++ -*-
2 *
3 * This file is a part of LEMON, a generic C++ optimization library
4 *
5 * Copyright (C) 2003-2006
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
29namespace 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 prio) {
154      return prio < boxes[box].min;
155    }
156
157    bool lower(int box, Prio prio) {
158      return prio >= 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 size = 2 * boxes.back().size;
191      boxes.push_back(RadixBox(min, size));
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 prio) {
204      while (lower(start, prio)) {
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 prio) {
222      while (upper(start, prio)) {
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
Note: See TracBrowser for help on using the repository browser.