Skip to main content
Code Review

Return to Question

Tweeted twitter.com/StackCodeReview/status/928474692104704000
replaced http://codereview.stackexchange.com/ with https://codereview.stackexchange.com/
Source Link

Edit: Forgot to mention, sift_up and sift_down algorithms are on separate review here here

Edit: Forgot to mention, sift_up and sift_down algorithms are on separate review here

Edit: Forgot to mention, sift_up and sift_down algorithms are on separate review here

added 178 characters in body
Source Link
Ilya Popov
  • 468
  • 2
  • 8

Edit: Forgot to mention, sift_up and sift_down algorithms are on separate review here

Edit: Forgot to mention, sift_up and sift_down algorithms are on separate review here

Source Link
Ilya Popov
  • 468
  • 2
  • 8

Updatable priority queue

This is an updatable priority queue. In contains an index (implemented as std::unordered_map) which maps a value to its position in the queue itself. The queue is implemented as a binary heap in a std::vector. This class stores each value twice: once in the queue and once in the index. Internally it uses a self-reporting type: an object which reports all its movements to headquarters:

template<typename V>
class self_reporting
{
public:
 using value_type = V;
 self_reporting(const value_type & v, self_reporting** pp)
 : value(v), registry(pp)
 {
 report();
 }
 self_reporting(value_type && v, self_reporting** pp)
 : value(std::move(v)), registry(pp)
 {
 report();
 }
 ~self_reporting()
 {
 if (registry)
 *registry = nullptr;
 }
 self_reporting(const self_reporting &) = delete;
 self_reporting(self_reporting && other)
 : value(std::move(other.value)), registry(other.registry)
 {
 other.registry = nullptr;
 report();
 }
 self_reporting & operator=(const self_reporting &) = delete;
 self_reporting & operator=(self_reporting && other)
 {
 value = std::move(other.value);
 registry = other.registry;
 other.registry = nullptr;
 report();
 return *this;
 }
 const value_type & get() const
 {
 return value;
 }
 value_type & get()
 {
 return value;
 }
 void swap(self_reporting & other)
 {
 using std::swap;
 swap(value, other.value);
 swap(*registry, *other.registry);
 swap(registry, other.registry);
 }
private:
 void report()
 {
 if (registry)
 *registry = this;
 }
 value_type value;
 self_reporting **registry;
};
template<typename V>
inline void swap(self_reporting<V> & lhs, self_reporting<V> & rhs)
{
 lhs.swap(rhs);
}

Updatable priority queue:

template <typename V, typename P, typename Cmp = std::less<P>>
class updatable_priority_queue
{
public:
 using value_type = V;
 using priority_type = P;
 using priority_compare = Cmp;
 struct value_priority
 {
 value_type value;
 priority_type priority;
 };
 using element_type = value_priority;
 using reference = element_type &;
 using const_reference = element_type const &;
 using queue_element_type = self_reporting<element_type>;
 using queue_type = std::vector<queue_element_type>;
 using index_type = std::unordered_map<value_type, queue_element_type*>;
 updatable_priority_queue() = default;
 updatable_priority_queue(const priority_compare & cmp)
 : cmp_(cmp)
 {}
 struct priority_comparator
 {
 priority_compare & cmp_;
 bool operator()(const queue_element_type & lhs,
 const queue_element_type & rhs) const
 {
 return cmp_(lhs.get().priority, rhs.get().priority);
 }
 };
 size_t size() const
 {
 assert(queue_.size() == index_.size());
 return queue_.size();
 }
 bool empty() const
 {
 assert(queue_.size() == index_.size());
 return queue_.empty();
 }
 const_reference top() const
 {
 return queue_.front().get();
 }
 bool insert(const value_type & value, const priority_type & priority)
 {
 assert(std::is_heap(queue_.begin(), queue_.end(), priority_comparator{cmp_}));
 auto r = index_.emplace(value, nullptr);
 if (!r.second)
 return false;
 queue_.emplace_back(element_type{value, priority}, &(r.first->second));
 std::push_heap(queue_.begin(), queue_.end(), priority_comparator{cmp_});
 assert(std::is_heap(queue_.begin(), queue_.end(), priority_comparator{cmp_}));
 return true;
 }
 void pop()
 {
 assert(!empty());
 assert(std::is_heap(queue_.begin(), queue_.end(), priority_comparator{cmp_}));
 value_type v = top().value;
 std::pop_heap(queue_.begin(), queue_.end(), priority_comparator{cmp_});
 queue_.pop_back();
 index_.erase(v);
 assert(std::is_heap(queue_.begin(), queue_.end(), priority_comparator{cmp_}));
 }
 bool exists(const value_type & value) const
 {
 return index_.find(value) != index_.end();
 }
 bool update(const value_type & value, const priority_type & new_priority)
 {
 assert(std::is_heap(queue_.begin(), queue_.end(), priority_comparator{cmp_}));
 auto i = index_.find(value);
 if (i == index_.end())
 return false;
 queue_element_type * p = i->second;
 if (cmp_(p->get().priority, new_priority))
 {
 p->get().priority = new_priority;
 sift_up(queue_.begin(), queue_.end(),
 queue_.begin() + (p - &queue_[0]), priority_comparator{cmp_});
 }
 else
 {
 p->get().priority = new_priority;
 sift_down(queue_.begin(), queue_.end(),
 queue_.begin() + (p - &queue_[0]), priority_comparator{cmp_});
 }
 assert(std::is_heap(queue_.begin(), queue_.end(), priority_comparator{cmp_}));
 return true;
 }
private:
 index_type index_;
 queue_type queue_;
 priority_compare cmp_;
};

Usage example:

#include "updatable_priority_queue.hpp"
#include <iostream>
#include <string>
int main()
{
 updatable_priority_queue<std::string, int, std::greater<int>> q;
 q.insert("six", 6);
 q.insert("seven", 7);
 q.insert("eight", 8);
 q.insert("nine", 9);
 q.insert("ten", 10);
 q.insert("one", 1);
 q.insert("two", 2);
 q.insert("three", 3);
 q.insert("four", 4);
 q.insert("five", 5);
 assert(q.size() == 10);
 assert(q.exists("six"));
 assert(!q.insert("four", 10));
 // sift up
 q.update("seven", 0);
 q.update("five", 5);
 q.update("six", 4);
 // sift down
 q.update("one", 11);
 q.update("four", 4);
 q.update("three", 6);
 while (!q.empty())
 {
 std::cout << q.top().value << '\t' << q.top().priority << std::endl;
 q.pop();
 }
}
lang-cpp

AltStyle によって変換されたページ (->オリジナル) /