remove last added element from priority_queue
Can anyone think of a slick way to remove the last added element from
a priority_queue.
regardless of the compare function
I want to do this for exception safety, so it should not be needed
very often.
I am thinking I am probably going to have to reimplement the
queue myself to get this functionality
[337 byte] By [
souldog] at [2007-11-18 19:17:51]

# 1 Re: remove last added element from priority_queue
I think having a pointer to the last added element is probably the most direct and simplest way. As long as you are not asking for n number of last added elements, there shouldn't be any problem.
Kheun at 2007-11-9 0:32:22 >

# 2 Re: remove last added element from priority_queue
I assume you mean you want to remove the element with
the lowest priority. Maybe I am wrong, but I don't see
anyway of doing this easily. The only thing I can think
of is :
1) create a new priority_queue of the same type
2) loop thru the original priority_queue
* retrieve element via top()
* pop()
* if (not empty)
add element retrieved via the top() to the new priority_queue
else
break out of loop
3) basically the same loop, but add all the elements in
the new container to the old one.
Maybe a better way would be to create your own class with
a deque as the internal container. If you have the Josuttis
book, I think the code in the "Class priority_queue<> in Detail"
could be used as is, just adding a function to remove the
first element.
# 3 Re: remove last added element from priority_queue
since no iteration is allowed on priority_queue,
I haven't found other way than Philip Nicoletti 's way.
to put it as code:
int main()
{
priority_queue <int, vector<int>, greater<int> > que;
priority_queue <int, vector<int>, greater<int> > tempQue;
que.push( 2 );
que.push( 1 );
que.push( 3 );
while ( que.size( ) > 1 )
{
tempQue.push(que.top( ));
que.pop( );
}
que=tempQue;
return 0;
}
Guysl at 2007-11-9 0:34:33 >

# 4 Re: remove last added element from priority_queue
First thanks for the responses.
I don't want to remove the member with the lowest priority, I
want to remove the member I just added regardless of its
priority. I am trying to implement some exception safety
for another wrapper
void InsertElementSetEvent(const T& element)
{
LOCKING_POINTER<WRAP_CONTAINER<T, C>, SYNC_CRITICAL_SECTION> locked_buffer(Buffer_, BufferCS_);
locked_buffer->AddElement(element);
SCOPE_GUARD guard = MakeGuard(REMOVE_ELEMENT_ON_FAILURE_HELPER<T, C>(&locked_buffer));
BufferEvent_.Set(); //throws
guard.Dismiss();
}
template<typename T, typename C>
class REMOVE_ELEMENT_ON_FAILURE_HELPER
{
private:
LOCKING_POINTER<WRAP_CONTAINER<T, C>, SYNC_CRITICAL_SECTION>* LockedObj_;
public:
REMOVE_ELEMENT_ON_FAILURE_HELPER(LOCKING_POINTER<WRAP_CONTAINER<T, C>, SYNC_CRITICAL_SECTION>* locked_obj):LockedObj_(locked_obj){}
void operator()()
{
(*LockedObj_)->RemoveLastAddedElement();
}
};
Here I am just using ScopeGuard to execute a functor (I used
a functor here because that is the way I found to get the thing
to compile in VC++)
The details of this wrapper I posted in another thread, although
I have changed it somewhat, based on all the feedback I got there:)
I will just write my own priority queue
# 5 Re: remove last added element from priority_queue
It can be a pain though, since usually priority queues are built using heaps and removing any random element from a heap is not that easy. If you don't mind a bit of extra memory overhead then using a std::set may be easier.
# 6 Re: remove last added element from priority_queue
Well I gave it a shot with heaps, tell me what you think
template<typename Tp>
class in_order_compare
{
public:
bool operator()(const pair<unsigned long, Tp>& rhs, const pair<unsigned long, Tp>& lhs) const
{
return rhs.first < lhs.first;
}
};
template<typename _Tp, typename _Compare>
class wrap_compare
{
private:
_Compare Comp_;
public:
wrap_compare(_Compare _x):Comp_(_x){}
bool operator()(const pair<unsigned long, _Tp>& rhs, const pair<unsigned long, _Tp>& lhs) const
{
return Comp_(rhs.second, lhs.second);
}
};
template <typename _Tp, typename _Compare>
class my_priority_queue {
protected:
vector<pair<unsigned long, _Tp> > c;
_Compare comp;
unsigned long InOrder_;
public:
my_priority_queue():c(), InOrder_(0){}
bool empty() const { return c.empty(); }
vector<pair<unsigned long, _Tp> >::size_type size() const { return c.size(); }
const _Tp& top() const { return c.front().second; }
void push(const _Tp& __x)
{
c.push_back(make_pair(InOrder_++,__x));
push_heap(c.begin(), c.end(), wrap_compare<_Tp, _Compare>(comp));
}
void pop()
{
pop_heap(c.begin(), c.end(), wrap_compare<_Tp, _Compare>(comp));
c.pop_back();
if(c.empty())
InOrder_ = 0;
}
void pop_last_queued()
{
make_heap(c.begin(), c.end(), in_order_compare<_Tp>());
pop_heap(c.begin(), c.end(), in_order_compare<_Tp>());
c.pop_back();
make_heap(c.begin(), c.end(), wrap_compare<_Tp, _Compare>(comp));
if(c.empty())
InOrder_ = 0;
}
};
Actually I am a little disturbed at this check for empty on each pop
[Yves: Someday I'll stone you to death for using the PHP tags which make code really long and hard to read]
# 7 Re: remove last added element from priority_queue
Yeah that looks fine, assuming that you won't need to have to worry about the range of ints for you InOrder_ counter (i.e. if your objects exists for a long time and tends not to be empty, you should worry).
Another note is that you shouldn't use variable names and template parameters that begin with an underscore. Those are reserved for the standard library.
The call to c.empty() is OK, it's probably the fastest thing in the whole pop function, and will be inlined by any good compiler.
Ah, yes, and I wouldn't call in_order_compare that. I would make it a tad more generic by having two template parameters and call it something like pair_less_than_on_first. But well, that's a matter of taste.
A more subtle thing is that you should guard the heap functions (i.e. pop_heap and your top) against being called on an empty container. The precondition for pop_heap is that the range is non-empty and of course vector::top will throw an exception if you call it on an empty vector.
# 8 Re: remove last added element from priority_queue
But yves, I love the pretty colors...
1. The problem you mentioned about overflowing int does
reallly bug me, while I am 99% percent sure that it will not be
a problem, it just is ugly to have the possibility.
Do you have any suggestions?
2. Yah, I pulled the code for the priority_queue out of STLPort
and modified
3. I agree I will make the first template function more generic
4. I was aware of the problem with accessing an empty container
but I will check that in a higher level wrapper. This gives me the
option to not check.
Thanks for all the imput Yves :thumb:
# 9 Re: remove last added element from priority_queue
You want pretty colors, you use my syntax highlighter :D No seriously, the problem with the empty lines with the php tags makes stuff unreadable for me at the least.
One solution for the overflow is to periodically rebase the entire queue. You could keep a counter and say every million's push, you serialise the counter values used in the current vector and reset InOrder_ to be one more than the maximum.
# 10 Re: remove last added element from priority_queue
Of course. Thanks Yves.
# 11 Re: remove last added element from priority_queue
Or easier, just test InOrder itself and check whether the second to highest bit is set. (i.e. if (InOrder_ & 0x40000000))
# 12 Re: remove last added element from priority_queue
Good Idea, I was using a constant member to compare InOrder_ to
here is the next attempt
using namespace std;
#include <vector>
#include <algorithm>
#include <limits>
#include <stdexcept>
template<typename K, typename T, typename Compare = less<K> >
class pair_less_on_first
{
private:
Compare Comp_;
public:
pair_less_on_first(Compare comp = less<K>()):Comp_(comp){}
bool operator()(const pair<K, T>& rhs, const pair<K, T>& lhs) const
{
return Comp_(rhs.first, lhs.first);
}
};
template<typename K, typename T, typename Compare = less<T> >
class pair_less_on_second
{
private:
Compare Comp_;
public:
pair_less_on_second(Compare comp = less<T>()):Comp_(comp){}
bool operator()(const pair<K, T>& rhs, const pair<K, T>& lhs) const
{
return Comp_(rhs.second, lhs.second);
}
};
template <typename T, typename CompareT = less<T> >
class my_priority_queue {
protected:
vector<pair<unsigned long, T> > C_;
CompareT CompT_;
unsigned long InOrder_;
void RebaseInOrder()
{
make_heap(C_.begin(), C_.end(), pair_less_on_first<unsigned long, T>());
sort_heap(C_.begin(), C_.end(), pair_less_on_first<unsigned long, T>());
for(vector<pair<unsigned long, T> >::size_type i = 0; i < C_.size(); i++)
C_[i].first = i;
InOrder_ = C_.size() + 1;
make_heap(C_.begin(), C_.end(), pair_less_on_second<unsigned long, T, CompareT>(CompT_));
}
public:
my_priority_queue():C_(), InOrder_(0){}
bool empty() const { return C_.empty(); }
vector<pair<unsigned long, T> >::size_type size() const { return C_.size(); }
const T& top() const { return C_.front().second; }
void push(const T& t)
{
if(numeric_limits<unsigned long>::max() == InOrder_)
throw;
C_.push_back(make_pair(InOrder_++, t));
push_heap(C_.begin(), C_.end(), pair_less_on_second<unsigned long, T, CompareT>(CompT_));
if(InOrder_ & 0x40000000)
RebaseInOrder();
}
void pop()
{
pop_heap(C_.begin(), C_.end(), pair_less_on_second<unsigned long, T, CompareT>(CompT_));
C_.pop_back();
}
void pop_last_queued()
{
make_heap(C_.begin(), C_.end(), pair_less_on_first<unsigned long, T>());
pop_heap(C_.begin(), C_.end(), pair_less_on_first<unsigned long, T>());
C_.pop_back();
make_heap(C_.begin(), C_.end(), pair_less_on_second<unsigned long, T, CompareT>(CompT_));
}
};
# 13 Re: remove last added element from priority_queue
Your RebaseInOrder could be a bit simpler by just using std::sort and at the end you could set InOrder_ = C_.size(); (instead of +1)
Moreover, the line
if(numeric_limits<unsigned long>::max() == InOrder_)
throw;
Should be superflous. Remember that you are writing a container, so it's up to the container itself to guarantee integrity of its invariants.
As a side note, I would consider using typedefs for pair_less_on_first<unsigned long, T> and air_less_on_second<unsigned long, T, CompareT>.
# 14 Re: remove last added element from priority_queue
Originally posted by Yves M
Moreover, the line
if(numeric_limits<unsigned long>::max() == InOrder_)
throw;
Should be superflous. Remember that you are writing a container, so it's up to the container itself to guarantee integrity of its invariants.
Can you explain that a bit more
# 15 Re: remove last added element from priority_queue
Well, an invariant is a set of restrictions on the data that the container holds. As an example, for std::set, one of the invariants is that elements are always sorted. The invariant has to remain true after any function on the container has been called. So set::insert preserves the invariant by inserting the element in the right place. When set::insert is finished, the whole set is still sorted.
In your case, one of the invariants is that InOrder_ is always smaller than 0x40000000. This is guaranteed, since when you add an element (the only operation which could increase InOrder_) you check this and rebase accordingly, making InOrder_ smaller than that value again. So it doesn't make sense to check whether InOrder_ is equal to numeric_limits<unsigned long>::max() unless the size of an int could be smaller than 0x40000000. But in that case, you would have to modify this value anyhow (otherwise it would wrap around and the code would blow up).
Oh yes, sorry, the check
if (InOrder & 0x40000000)
is equivalent in this case to:
if (InOrder >= 0x40000000)
# 16 Re: remove last added element from priority_queue
well I do rebase, but if it happens to be the case that there are
actually 0x40000000 elements or more in the queue, then that number will be exceeded. So that is not an invariant. Actually this is a problem.
# 17 Re: remove last added element from priority_queue
Didn't see your edit, so would you agree that throwing the exception is needed here?
I guess there could be problem with this design is the queue size
actually exceeds 0x40000000, it will be rebasing all the time.
# 18 Re: remove last added element from priority_queue
Well, that would mean at least one gigabyte of data (assuming each data item has only one byte), but if you want to allow for that, then you'll have to use a bigger datatype for InOrder_ or use 0x80000000 (which works, since you're using unsigned longs) which will allow up to at least 2 Gibaytes. On a IA32 machine you can't get much further than that anyways.
I guess there could be problem with this design is the queue size
actually exceeds 0x40000000, it will be rebasing all the time.
Do you really expect to hold that many items at one single time? Note that a vector will only be able to hold up to 0xFFFFFFFF items on an IA32 machine, but my guess is that you run out of memory way before that.
# 19 Re: remove last added element from priority_queue
nope, I don't expect this queue to hold more than say 10 items
at a time, I just don't want to code it making assumptions like that.
So I guess to throw an exception here is appropriate and the only
full proof thing to do.
# 20 Re: remove last added element from priority_queue
I am probably just over engineering this:)
# 21 Re: remove last added element from priority_queue
Then throw the exception when C_.size() is bigger than 0x40000000. That makes more sense.
# 22 Re: remove last added element from priority_queue
Yes it does
Thanks Yves
# 23 Re: remove last added element from priority_queue
anyway, here is the final little queue
Thanks once again Yves
using namespace std;
#include <vector>
#include <algorithm>
#include <limits>
#include <stdexcept>
template<typename K, typename T, typename Compare = less<K> >
class pair_less_on_first
{
private:
Compare Comp_;
public:
pair_less_on_first(Compare comp = Compare()):Comp_(comp){}
bool operator()(const pair<K, T>& rhs, const pair<K, T>& lhs) const
{
return Comp_(rhs.first, lhs.first);
}
};
template<typename K, typename T, typename Compare = less<T> >
class pair_less_on_second
{
private:
Compare Comp_;
public:
pair_less_on_second(Compare comp = Compare ()):Comp_(comp){}
bool operator()(const pair<K, T>& rhs, const pair<K, T>& lhs) const
{
return Comp_(rhs.second, lhs.second);
}
};
template <typename T, typename CompareT = less<T> >
class my_priority_queue {
public:
typedef vector<pair<unsigned long, T> > Container;
typedef pair_less_on_first<unsigned long, T> CompareFirst;
typedef pair_less_on_second<unsigned long, T, CompareT> CompareSecond;
protected:
Container C_;
CompareSecond Comp2nd_;
unsigned long InOrder_;
void RebaseInOrder()
{
sort(C_.begin(), C_.end(), CompareFirst());
for(Container::size_type i = 0; i < C_.size(); i++)
C_[i].second = i;
InOrder_ = C_.size();
make_heap(C_.begin(), C_.end(), Comp2nd_);
}
public:
my_priority_queue():C_(), InOrder_(0){}
bool empty() const { return C_.empty(); }
vector<pair<unsigned long, T> >::size_type size() const { return C_.size(); }
const T& top() const { return C_.front().second; }
void push(const T& t)
{
if(0x40000000 == InOrder_)
throw;
C_.push_back(make_pair(InOrder_++, t));
push_heap(C_.begin(), C_.end(), Comp2nd_);
if(InOrder_ == 0x40000000)
RebaseInOrder();
}
void pop()
{
pop_heap(C_.begin(), C_.end(), Comp2nd_);
C_.pop_back();
}
void pop_last_queued()
{
make_heap(C_.begin(), C_.end(), CompareFirst());
pop_heap(C_.begin(), C_.end(), CompareFirst());
C_.pop_back();
make_heap(C_.begin(), C_.end(), Comp2nd_); }
};
# 24 Re: remove last added element from priority_queue
I have a question about your constructor in your comparison functions. Currently it's:
pair_less_on_first(Compare comp = less<K>()):Comp_(comp){}
Wouldn't it make more sense to make them:
pair_less_on_first(const Compare &comp = Compare()):Comp_(comp){}
Also, instead of having a variable that holds Compare, it may be an idea to hold one that has CompareSecond(CompT_), since that's all you use.
# 25 Re: remove last added element from priority_queue
Yup, right agian