Advertisement
froleyks

indexed_priority_queue.hpp

Feb 2nd, 2019
207
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 5.00 KB | None | 0 0
  1. #pragma once
  2.  
  3. #include <cstddef>
  4. #include <functional>
  5. #include <utility>
  6. #include <vector>
  7.  
  8. /**
  9.  * A priority queue that is addressable by an index, i.e., a number
  10.  * from 0 to a user-specified maximum.
  11.  * K is the key type, which must be an integral type.
  12.  * V is the value type, which must be default constructible and movable.
  13.  * Comp is the comparison operator.
  14.  *
  15.  * NOTE: This is a max-heap, i.e., top() is the largest element in the queue.
  16.  */
  17. template <class K, class V, class Comp = std::less<>>
  18. class IndexedPriorityQueue {
  19.  public:
  20.     /**
  21.      * (Default) constructor.
  22.      * capacity is the maximum number of keys that can be inserted.
  23.      */
  24.     IndexedPriorityQueue(std::size_t capacity = 0, Comp comp = {})
  25.             : index_(capacity), comp_(std::move(comp)) {}
  26.  
  27.     /**
  28.      * Returns the maximum number of keys that can be inserted.
  29.      */
  30.     std::size_t capacity() const { return index_.size(); }
  31.  
  32.     /**
  33.      * Increases the maximum number of keys that can be inserted.
  34.      */
  35.     void reserve(std::size_t capacity) {
  36.         if (capacity > index_.size()) index_.resize(capacity);
  37.     }
  38.  
  39.     /**
  40.      * Returns true if the queue contains no elements.
  41.      */
  42.     bool empty() const { return heap_.size() == 1; }
  43.  
  44.     /**
  45.      * Returns the number of elements in the queue.
  46.      */
  47.     std::size_t size() const { return heap_.size() - 1; }
  48.  
  49.  
  50.     /**
  51.      * Returns the largest element in the queue.
  52.      * The queue must not be empty.
  53.      */
  54.     const std::pair<K, V>& top() const { return heap_[1]; }
  55.  
  56.     /**
  57.      * Returns true if the given key exists in the queue.
  58.      */
  59.     bool hasKey(K key) const { return index_[key] != 0; }
  60.  
  61.     /**
  62.      * Returns the priority for the given key.
  63.      * The key must exist in the queue.
  64.      */
  65.     const V& priority(K key) const { return heap_[index_[key]].second; }
  66.  
  67.  
  68.     /**
  69.      * Inserts a new key with the given value into the queue.
  70.      * The key must not already exist in the queue.
  71.      */
  72.     void push(K key, V value) {
  73.         heap_.emplace_back();
  74.         siftUp(heap_.size() - 1, key, std::move(value));
  75.     }
  76.  
  77.     /**
  78.      * Changes the value for the given key.
  79.      * The key must exist in the queue.
  80.      */
  81.     void changePriority(K key, V new_value) {
  82.         if (comp_(new_value, priority(key)))
  83.             siftDown(index_[key], key, std::move(new_value));
  84.         else
  85.             siftUp(index_[key], key, std::move(new_value));
  86.     }
  87.  
  88.     /**
  89.      * Inserts the key if it does not yet exist, or changes its priority if it does.
  90.      */
  91.     void pushOrChangePriority(K key, V new_value) {
  92.         if (hasKey(key))
  93.             changePriority(key, std::move(new_value));
  94.         else
  95.             push(key, std::move(new_value));
  96.     }
  97.  
  98.     /**
  99.      * Removes the largest element from the queue.
  100.      * The queue must not be empty.
  101.      */
  102.     std::pair<K, V> pop() {
  103.         auto r = std::move(heap_[1]);
  104.         index_[r.first] = 0;
  105.         siftDown(1, heap_.back().first, std::move(heap_.back().second));
  106.         heap_.pop_back();
  107.         return r;
  108.     }
  109.  
  110.  private:
  111.     // the heap, indexing starts at 1, index 0 is unused
  112.     std::vector<std::pair<K, V>> heap_{1};
  113.     // mapping from keys to indices in the heap
  114.     std::vector<K> index_;
  115.     Comp comp_;
  116.  
  117.     // performs sift-up of (key, value) starting at index i
  118.     void siftUp(std::size_t i, K key, V&& value) {
  119.         std::size_t parent = i / 2;
  120.  
  121.         // move hole upwards until correct position found
  122.         while (parent > 0 && comp_(heap_[parent].second, value)) {
  123.             heap_[i] = std::move(heap_[parent]);
  124.             index_[heap_[i].first] = i;
  125.             i = parent;
  126.             parent = i / 2;
  127.         }
  128.  
  129.         heap_[i] = {key, std::move(value)};
  130.         index_[key] = i;
  131.     }
  132.  
  133.     // performs sift-down of (key, value), starting at index 1
  134.     void siftDown(std::size_t i, K key, V&& value) {
  135.         std::size_t child = i * 2;
  136.  
  137.         const auto end = heap_.size() - 1;
  138.         while (child < end) {
  139.             // choose larger child
  140.             if (comp_(heap_[child].second, heap_[child + 1].second)) ++child;
  141.  
  142.             // stop if correct position found
  143.             if (!comp_(value, heap_[child].second)) break;
  144.  
  145.             // move hole downwards
  146.             heap_[i] = std::move(heap_[child]);
  147.             index_[heap_[i].first] = i;
  148.  
  149.             i = child;
  150.             child = 2 * i;
  151.         }
  152.  
  153.         // When sifting down the last element, the above loop is correct for all edge
  154.         // cases (i.e., if no early break from the loop).
  155.         // With r the root, l the last element, s its sibling, and p its parent:
  156.         // - r is l           =>  loop won't execute, i points to r, and
  157.         //                        the following is a no-op move (r = std::move(l))
  158.         // - s does not exist =>  i points to p, and the following is p = std::move(l)
  159.         // - l <= s           =>  i points to s, and the following is s = std::move(l)
  160.         // - l >  s           =>  early break, i points to p, and the following is p = std::move(l)
  161.  
  162.         heap_[i] = {key, std::move(value)};
  163.         index_[key] = i;
  164.     }
  165. };
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement