deba@728: /* -*- mode: C++; indent-tabs-mode: nil; -*- deba@728: * deba@728: * This file is a part of LEMON, a generic C++ optimization library. deba@728: * deba@728: * Copyright (C) 2003-2009 deba@728: * Egervary Jeno Kombinatorikus Optimalizalasi Kutatocsoport deba@728: * (Egervary Research Group on Combinatorial Optimization, EGRES). deba@728: * deba@728: * Permission to use, modify and distribute this software is granted deba@728: * provided that this copyright notice appears in all copies. For deba@728: * precise terms see the accompanying LICENSE file. deba@728: * deba@728: * This software is provided "AS IS" with no warranty of any kind, deba@728: * express or implied, and with no claim as to its suitability for any deba@728: * purpose. deba@728: * deba@728: */ deba@728: deba@728: #ifndef LEMON_RADIX_HEAP_H deba@728: #define LEMON_RADIX_HEAP_H deba@728: kpeter@757: ///\ingroup heaps deba@728: ///\file kpeter@756: ///\brief Radix heap implementation. deba@728: deba@728: #include deba@728: #include deba@728: deba@728: namespace lemon { deba@728: deba@728: kpeter@757: /// \ingroup heaps deba@728: /// kpeter@756: /// \brief Radix heap data structure. deba@728: /// kpeter@756: /// This class implements the \e radix \e heap data structure. kpeter@756: /// It practically conforms to the \ref concepts::Heap "heap concept", kpeter@756: /// but it has some limitations due its special implementation. kpeter@756: /// The type of the priorities must be \c int and the priority of an kpeter@756: /// item cannot be decreased under the priority of the last removed item. deba@728: /// kpeter@756: /// \tparam IM A read-writable item map with \c int values, used kpeter@756: /// internally to handle the cross references. deba@730: template deba@728: class RadixHeap { deba@728: deba@728: public: kpeter@756: kpeter@756: /// Type of the item-int map. kpeter@756: typedef IM ItemIntMap; kpeter@756: /// Type of the priorities. deba@728: typedef int Prio; kpeter@756: /// Type of the items stored in the heap. kpeter@756: typedef typename ItemIntMap::Key Item; deba@728: deba@728: /// \brief Exception thrown by RadixHeap. deba@728: /// kpeter@756: /// This exception is thrown when an item is inserted into a kpeter@756: /// RadixHeap with a priority smaller than the last erased one. deba@728: /// \see RadixHeap kpeter@758: class PriorityUnderflowError : public Exception { deba@728: public: deba@728: virtual const char* what() const throw() { kpeter@758: return "lemon::RadixHeap::PriorityUnderflowError"; deba@728: } deba@728: }; deba@728: kpeter@756: /// \brief Type to represent the states of the items. deba@728: /// kpeter@756: /// Each item has a state associated to it. It can be "in heap", kpeter@756: /// "pre-heap" or "post-heap". The latter two are indifferent from the deba@728: /// heap's point of view, but may be useful to the user. deba@728: /// kpeter@756: /// The item-int map must be initialized in such way that it assigns kpeter@756: /// \c PRE_HEAP (-1) to any element to be put in the heap. deba@728: enum State { kpeter@756: IN_HEAP = 0, ///< = 0. kpeter@756: PRE_HEAP = -1, ///< = -1. kpeter@756: POST_HEAP = -2 ///< = -2. deba@728: }; deba@728: deba@728: private: deba@728: deba@728: struct RadixItem { deba@728: int prev, next, box; deba@728: Item item; deba@728: int prio; deba@728: RadixItem(Item _item, int _prio) : item(_item), prio(_prio) {} deba@728: }; deba@728: deba@728: struct RadixBox { deba@728: int first; deba@728: int min, size; deba@728: RadixBox(int _min, int _size) : first(-1), min(_min), size(_size) {} deba@728: }; deba@728: kpeter@758: std::vector _data; kpeter@758: std::vector _boxes; deba@728: deba@730: ItemIntMap &_iim; deba@728: kpeter@756: public: deba@728: kpeter@756: /// \brief Constructor. deba@728: /// kpeter@756: /// Constructor. kpeter@756: /// \param map A map that assigns \c int values to the items. kpeter@756: /// It is used internally to handle the cross references. kpeter@756: /// The assigned value must be \c PRE_HEAP (-1) for each item. kpeter@756: /// \param minimum The initial minimum value of the heap. kpeter@756: /// \param capacity The initial capacity of the heap. kpeter@756: RadixHeap(ItemIntMap &map, int minimum = 0, int capacity = 0) kpeter@756: : _iim(map) kpeter@756: { kpeter@758: _boxes.push_back(RadixBox(minimum, 1)); kpeter@758: _boxes.push_back(RadixBox(minimum + 1, 1)); kpeter@758: while (lower(_boxes.size() - 1, capacity + minimum - 1)) { deba@728: extend(); deba@728: } deba@728: } deba@728: kpeter@756: /// \brief The number of items stored in the heap. deba@728: /// kpeter@756: /// This function returns the number of items stored in the heap. kpeter@758: int size() const { return _data.size(); } kpeter@756: kpeter@756: /// \brief Check if the heap is empty. deba@728: /// kpeter@756: /// This function returns \c true if the heap is empty. kpeter@758: bool empty() const { return _data.empty(); } deba@728: kpeter@756: /// \brief Make the heap empty. deba@728: /// kpeter@756: /// This functon makes the heap empty. kpeter@756: /// It does not change the cross reference map. If you want to reuse kpeter@756: /// a heap that is not surely empty, you should first clear it and kpeter@756: /// then you should set the cross reference map to \c PRE_HEAP kpeter@756: /// for each item. kpeter@756: /// \param minimum The minimum value of the heap. kpeter@756: /// \param capacity The capacity of the heap. kpeter@756: void clear(int minimum = 0, int capacity = 0) { kpeter@758: _data.clear(); _boxes.clear(); kpeter@758: _boxes.push_back(RadixBox(minimum, 1)); kpeter@758: _boxes.push_back(RadixBox(minimum + 1, 1)); kpeter@758: while (lower(_boxes.size() - 1, capacity + minimum - 1)) { deba@728: extend(); deba@728: } deba@728: } deba@728: deba@728: private: deba@728: deba@728: bool upper(int box, Prio pr) { kpeter@758: return pr < _boxes[box].min; deba@728: } deba@728: deba@728: bool lower(int box, Prio pr) { kpeter@758: return pr >= _boxes[box].min + _boxes[box].size; deba@728: } deba@728: kpeter@756: // Remove item from the box list deba@728: void remove(int index) { kpeter@758: if (_data[index].prev >= 0) { kpeter@758: _data[_data[index].prev].next = _data[index].next; deba@728: } else { kpeter@758: _boxes[_data[index].box].first = _data[index].next; deba@728: } kpeter@758: if (_data[index].next >= 0) { kpeter@758: _data[_data[index].next].prev = _data[index].prev; deba@728: } deba@728: } deba@728: kpeter@756: // Insert item into the box list deba@728: void insert(int box, int index) { kpeter@758: if (_boxes[box].first == -1) { kpeter@758: _boxes[box].first = index; kpeter@758: _data[index].next = _data[index].prev = -1; deba@728: } else { kpeter@758: _data[index].next = _boxes[box].first; kpeter@758: _data[_boxes[box].first].prev = index; kpeter@758: _data[index].prev = -1; kpeter@758: _boxes[box].first = index; deba@728: } kpeter@758: _data[index].box = box; deba@728: } deba@728: kpeter@756: // Add a new box to the box list deba@728: void extend() { kpeter@758: int min = _boxes.back().min + _boxes.back().size; kpeter@758: int bs = 2 * _boxes.back().size; kpeter@758: _boxes.push_back(RadixBox(min, bs)); deba@728: } deba@728: kpeter@756: // Move an item up into the proper box. kpeter@758: void bubbleUp(int index) { kpeter@758: if (!lower(_data[index].box, _data[index].prio)) return; deba@728: remove(index); kpeter@758: int box = findUp(_data[index].box, _data[index].prio); deba@728: insert(box, index); deba@728: } deba@728: kpeter@756: // Find up the proper box for the item with the given priority deba@728: int findUp(int start, int pr) { deba@728: while (lower(start, pr)) { kpeter@758: if (++start == int(_boxes.size())) { deba@728: extend(); deba@728: } deba@728: } deba@728: return start; deba@728: } deba@728: kpeter@756: // Move an item down into the proper box kpeter@758: void bubbleDown(int index) { kpeter@758: if (!upper(_data[index].box, _data[index].prio)) return; deba@728: remove(index); kpeter@758: int box = findDown(_data[index].box, _data[index].prio); deba@728: insert(box, index); deba@728: } deba@728: kpeter@756: // Find down the proper box for the item with the given priority deba@728: int findDown(int start, int pr) { deba@728: while (upper(start, pr)) { kpeter@758: if (--start < 0) throw PriorityUnderflowError(); deba@728: } deba@728: return start; deba@728: } deba@728: kpeter@756: // Find the first non-empty box deba@728: int findFirst() { deba@728: int first = 0; kpeter@758: while (_boxes[first].first == -1) ++first; deba@728: return first; deba@728: } deba@728: kpeter@756: // Gives back the minimum priority of the given box deba@728: int minValue(int box) { kpeter@758: int min = _data[_boxes[box].first].prio; kpeter@758: for (int k = _boxes[box].first; k != -1; k = _data[k].next) { kpeter@758: if (_data[k].prio < min) min = _data[k].prio; deba@728: } deba@728: return min; deba@728: } deba@728: kpeter@756: // Rearrange the items of the heap and make the first box non-empty deba@728: void moveDown() { deba@728: int box = findFirst(); deba@728: if (box == 0) return; deba@728: int min = minValue(box); deba@728: for (int i = 0; i <= box; ++i) { kpeter@758: _boxes[i].min = min; kpeter@758: min += _boxes[i].size; deba@728: } kpeter@758: int curr = _boxes[box].first, next; deba@728: while (curr != -1) { kpeter@758: next = _data[curr].next; kpeter@758: bubbleDown(curr); deba@728: curr = next; deba@728: } deba@728: } deba@728: kpeter@758: void relocateLast(int index) { kpeter@758: if (index != int(_data.size()) - 1) { kpeter@758: _data[index] = _data.back(); kpeter@758: if (_data[index].prev != -1) { kpeter@758: _data[_data[index].prev].next = index; deba@728: } else { kpeter@758: _boxes[_data[index].box].first = index; deba@728: } kpeter@758: if (_data[index].next != -1) { kpeter@758: _data[_data[index].next].prev = index; deba@728: } kpeter@758: _iim[_data[index].item] = index; deba@728: } kpeter@758: _data.pop_back(); deba@728: } deba@728: deba@728: public: deba@728: deba@728: /// \brief Insert an item into the heap with the given priority. deba@728: /// kpeter@756: /// This function inserts the given item into the heap with the kpeter@756: /// given priority. deba@728: /// \param i The item to insert. deba@728: /// \param p The priority of the item. kpeter@756: /// \pre \e i must not be stored in the heap. kpeter@756: /// \warning This method may throw an \c UnderFlowPriorityException. deba@728: void push(const Item &i, const Prio &p) { kpeter@758: int n = _data.size(); deba@730: _iim.set(i, n); kpeter@758: _data.push_back(RadixItem(i, p)); kpeter@758: while (lower(_boxes.size() - 1, p)) { deba@728: extend(); deba@728: } kpeter@758: int box = findDown(_boxes.size() - 1, p); deba@728: insert(box, n); deba@728: } deba@728: kpeter@756: /// \brief Return the item having minimum priority. deba@728: /// kpeter@756: /// This function returns the item having minimum priority. kpeter@756: /// \pre The heap must be non-empty. deba@728: Item top() const { deba@728: const_cast&>(*this).moveDown(); kpeter@758: return _data[_boxes[0].first].item; deba@728: } deba@728: kpeter@756: /// \brief The minimum priority. deba@728: /// kpeter@756: /// This function returns the minimum priority. kpeter@756: /// \pre The heap must be non-empty. deba@728: Prio prio() const { deba@728: const_cast&>(*this).moveDown(); kpeter@758: return _data[_boxes[0].first].prio; deba@728: } deba@728: kpeter@756: /// \brief Remove the item having minimum priority. deba@728: /// kpeter@756: /// This function removes the item having minimum priority. deba@728: /// \pre The heap must be non-empty. deba@728: void pop() { deba@728: moveDown(); kpeter@758: int index = _boxes[0].first; kpeter@758: _iim[_data[index].item] = POST_HEAP; deba@728: remove(index); kpeter@758: relocateLast(index); deba@728: } deba@728: kpeter@756: /// \brief Remove the given item from the heap. deba@728: /// kpeter@756: /// This function removes the given item from the heap if it is kpeter@756: /// already stored. kpeter@756: /// \param i The item to delete. kpeter@756: /// \pre \e i must be in the heap. deba@728: void erase(const Item &i) { deba@730: int index = _iim[i]; deba@730: _iim[i] = POST_HEAP; deba@728: remove(index); kpeter@758: relocateLast(index); deba@728: } deba@728: kpeter@756: /// \brief The priority of the given item. deba@728: /// kpeter@756: /// This function returns the priority of the given item. deba@728: /// \param i The item. kpeter@756: /// \pre \e i must be in the heap. deba@728: Prio operator[](const Item &i) const { deba@730: int idx = _iim[i]; kpeter@758: return _data[idx].prio; deba@728: } deba@728: kpeter@756: /// \brief Set the priority of an item or insert it, if it is kpeter@756: /// not stored in the heap. deba@728: /// kpeter@756: /// This method sets the priority of the given item if it is kpeter@756: /// already stored in the heap. Otherwise it inserts the given kpeter@756: /// item into the heap with the given priority. deba@728: /// \param i The item. deba@728: /// \param p The priority. kpeter@756: /// \pre \e i must be in the heap. kpeter@756: /// \warning This method may throw an \c UnderFlowPriorityException. deba@728: void set(const Item &i, const Prio &p) { deba@730: int idx = _iim[i]; deba@728: if( idx < 0 ) { deba@728: push(i, p); deba@728: } kpeter@758: else if( p >= _data[idx].prio ) { kpeter@758: _data[idx].prio = p; kpeter@758: bubbleUp(idx); deba@728: } else { kpeter@758: _data[idx].prio = p; kpeter@758: bubbleDown(idx); deba@728: } deba@728: } deba@728: kpeter@756: /// \brief Decrease the priority of an item to the given value. deba@728: /// kpeter@756: /// This function decreases the priority of an item to the given value. deba@728: /// \param i The item. deba@728: /// \param p The priority. kpeter@756: /// \pre \e i must be stored in the heap with priority at least \e p. kpeter@756: /// \warning This method may throw an \c UnderFlowPriorityException. deba@728: void decrease(const Item &i, const Prio &p) { deba@730: int idx = _iim[i]; kpeter@758: _data[idx].prio = p; kpeter@758: bubbleDown(idx); deba@728: } deba@728: kpeter@756: /// \brief Increase the priority of an item to the given value. deba@728: /// kpeter@756: /// This function increases the priority of an item to the given value. deba@728: /// \param i The item. deba@728: /// \param p The priority. kpeter@756: /// \pre \e i must be stored in the heap with priority at most \e p. deba@728: void increase(const Item &i, const Prio &p) { deba@730: int idx = _iim[i]; kpeter@758: _data[idx].prio = p; kpeter@758: bubbleUp(idx); deba@728: } deba@728: kpeter@756: /// \brief Return the state of an item. deba@728: /// kpeter@756: /// This method returns \c PRE_HEAP if the given item has never kpeter@756: /// been in the heap, \c IN_HEAP if it is in the heap at the moment, kpeter@756: /// and \c POST_HEAP otherwise. kpeter@756: /// In the latter case it is possible that the item will get back kpeter@756: /// to the heap again. deba@728: /// \param i The item. deba@728: State state(const Item &i) const { deba@730: int s = _iim[i]; deba@728: if( s >= 0 ) s = 0; deba@728: return State(s); deba@728: } deba@728: kpeter@756: /// \brief Set the state of an item in the heap. deba@728: /// kpeter@756: /// This function sets the state of the given item in the heap. kpeter@756: /// It can be used to manually clear the heap when it is important kpeter@756: /// to achive better time complexity. deba@728: /// \param i The item. deba@728: /// \param st The state. It should not be \c IN_HEAP. deba@728: void state(const Item& i, State st) { deba@728: switch (st) { deba@728: case POST_HEAP: deba@728: case PRE_HEAP: deba@728: if (state(i) == IN_HEAP) { deba@728: erase(i); deba@728: } deba@730: _iim[i] = st; deba@728: break; deba@728: case IN_HEAP: deba@728: break; deba@728: } deba@728: } deba@728: deba@728: }; // class RadixHeap deba@728: deba@728: } // namespace lemon deba@728: deba@728: #endif // LEMON_RADIX_HEAP_H