Code Review Stack Exchange is a question and answer site for peer programmer code reviews. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

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();
    }
}

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

share|improve this question

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Browse other questions tagged or ask your own question.