Easiest way of using min priority queue with key update in C++

前端 未结 4 1310
终归单人心
终归单人心 2020-12-02 04:20

Sometimes during programming contests etc., we need a simple working implementation of min priority queue with decrease-key to implement Dijkstra algorithm etc.. I often use

4条回答
  •  鱼传尺愫
    2020-12-02 04:40

    Actually, there is a way to use built-in binary heap from the standard c++ library. The key observation is that the implementation of all heap functions (i.e. std::push_heap, std::pop_heap and std::make_heap) need only the following methods from the stored element of class A:

    • Constructor A::A()
    • Assignment operator A& A::operator=(const A& rhs)
    • Comparison operator bool operator<(const A& lhs, const A& rhs)

    It means that assignment operator is called whenever an element is moved in the container storing all heap elements. By overloading this operator you can control the index in the heap. When you have the index you can access the element in the heap, change its value and call std::push_heap to update its position in the heap.

    See the simplified implementation of Dijkstra algorithm (without graph):

    #include 
    
    using namespace std;
    
    vector queue_idx;
    
    struct Elem {
        int label;
        int dist;
        bool operator<(const Elem& other) const { return dist > other.dist; }
        Elem& operator=(const Elem& other);
    };
    
    vector q;
    
    Elem& Elem::operator=(const Elem& other)
    {
        label = other.label;
        dist = other.dist;
        queue_idx[label] = this - q.data();
        return *this;
    }
    
    void AddElem(int label, int dist)
    {
        queue_idx[label] = label;
        q.push_back(Elem{label, dist});
    }
    
    void RemoveMin()
    {
        pop_heap(q.begin(), q.end());
        Elem res = q.back();
        q.pop_back();
        cout << "dist to " << res.label << " is " << res.dist << endl;
    }
    
    void Relax(int label, int dist)
    {
        int idx = queue_idx[label];
        Elem& elem = q[idx];
        if (elem.dist > dist)
        {
            elem.dist = dist;
            push_heap(q.begin(), q.begin() + idx + 1);
        }
    }
    
    int main()
    {
        int n = 5;
        queue_idx.resize(n);
        AddElem(0, 0);
        for (int i = 1; i < n; ++i)
            AddElem(i, INT_MAX);
        make_heap(q.begin(), q.end());
        RemoveMin();
        Relax(1, 50);
        Relax(2, 40);
        Relax(4, 10);
        RemoveMin();
        Relax(3, 20);
        RemoveMin();
        Relax(1, 30);
        RemoveMin();
        Relax(2, 80);
        RemoveMin();
        return 0;
    }
    

    I know that this solution depends on the inner implementation of the standard library, however it just works for any compiler I am aware of and I used it in programming competitions.

提交回复
热议问题