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