Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ priority queue swapping contents

I am writing a class which has three priority queues as private members.

class Foo {
...
...

private:
  // I am fine with using pointers instead if it helps.
  std::priority_queue<int> first;   // min heap.
  std::priority_queue<int> second;  // max heap.
  std::priority_queue<int> third;  // min heap.
};

Now I require first and third to start as min heaps and second as a max heap. As part of the functionality of my class I need to do the following:

  1. Move second to first. Ideally this is achieved through lowest amount of copying. The underlying vector should just be moved. Additionally first should now behave like a max heap.
  2. Move third to second. This means second should now behave like a min heap.
  3. Since third's contents have been moved to second, it should be empty. I would like to either allocate a new underlying vector or re-use first's underlying vector (it doesn't need it any more. Additionally third should now be a max heap.

I need to perform this cycle (max -> min and min -> max) an unknown number of times.

I am struggling to do this with std::priority_queue since the Comparator is a template argument which means I cannot change it at run time. This is preventing me from turning a min heap into a max heap.

So my questions are:

  1. Is there a way I could possibly bend std::priority_queue to do my bidding without making it extremely ugly?
  2. If not then could I perhaps re-structure my class to do the same thing, but still use std::priority_queue?
  3. Otherwise could I maybe re-use most of the heapify logic in the std library to achieve this?
like image 812
Rajiv Avatar asked May 19 '14 06:05

Rajiv


People also ask

Are duplicate values allowed in priority queue?

Yes, in C++ priority_queue, we may have duplicate values.

How do I copy from one priority queue to another?

priority_queue::swap() This function is used to swap the contents of one priority queue with another priority queue of same type and size.

Why deletion operation is optimum in priority queue?

Deletion in Priority Queue: As you know that in a max heap, the maximum element is the root node. And it will remove the element which has maximum priority first. Thus, you remove the root node from the queue. This removal creates an empty slot, which will be further filled with new insertion.


2 Answers

Is there a way I could possibly bend std::priority_queue to do my bidding without making it extremely ugly?

You could write a wrapper that hides the predicate and uses inheritance behind the scenes. However, that seems overkill.

If not then could I perhaps re-structure my class to do the same thing, but still use std::priority_queue?

You could wrap the access to the queues in functions. Then use a bool or integer variable to check which queue needs to be accessed.

Otherwise could I maybe re-use most of the heapify logic in the std library to achieve this?

This sounds like the best option, based on what you explained. Store each priority_queue in a std::vector and use the std::make_heap, std::push_heap and std::pop_heap functions to manage the heap structure. If you keep all priority queues in a std::array<std::vector<int>, 3>, you can use std::rotate to perform the logic you described. In addition, you would need to keep a boolean variable indicating which predicate to use for the heap operations.

like image 92
D Drmmr Avatar answered Nov 03 '22 00:11

D Drmmr


A comparator with state

Actually, the STL provides facilities for exactly your case: You can pass a comparator to the constructor of the priority queue. The key idea is to give comparator some internal state which determines whether a less than or greater than operation should be applied. The comparator type looks like this:

struct LessOrGreater
{
    explicit LessOrGreater( bool isLess ) : isLess{isLess} {}

    bool operator()( int lhs, int rhs ) const
    {
        return isLess == (lhs<rhs);
    }

    bool isLess;
};

The actual type of the priority queue is

using MinOrMaxQueue =
    std::priority_queue<int,std::vector<int>,LessOrGreater>;

The class implementation

Your class can now be implemented in terms of this special priority queue.

class Foo {
public:
    Foo()
        : first { LessOrGreater{ false } } // initialize as min heap
        , second{ LessOrGreater{ true  } } // initialize as max heap
        , third { LessOrGreater{ false } } // initialize as min heap
    {}

    void op(); // The operation you explained

private:
    MinOrMaxQueue first; 
    MinOrMaxQueue second;
    MinOrMaxQueue third; 
};

Now the operation you described could be implemented like this:

void Foo::op()
{
    first  = std::move(second);
    second = std::move(third);
    third  = MinOrMaxQueue{ LessOrGreater{ true } }; // might throw!
}

Making it exception-safe

However, this code is not exception-safe. Since the default constructor of std::vector<int> might throw (the C++ standard does not guarantee no-fail here!) the third line of the op() function could throw leaving the Foo object in an invalid state. Here's an implementation that is strongly exception-safe and most likely just as efficient:

void Foo::op()
{
    MinOrMaxQueue tmp{ LessOrGreater{ true } };
    first .swap( second );
    second.swap( third );
    third .swap( tmp );
}

The first line is the only line that could possibly throw but it does not modify the Foo object. So throwing does cannot possibly damage anything. The remaining three lines never throw and hence the function is strongly exception-safe.

like image 38
Ralph Tandetzky Avatar answered Nov 03 '22 01:11

Ralph Tandetzky