I\'m using a priority queue as a scheduler with one extra requirement. I need to be able to cancel scheduled items. This equates to removing an item from the middle of the pri
You can try ‘std::multiset’ which is implemented as the heap structure and support ‘std::erase’ operation, so you could ‘std::find’ the element then erase it.
I guess you know which element in the heap container (index n) you want to delete.
v[n] = BIG;
the value BIG
is really bigger than any other values in the heap.std::push_heap( v.begin(), v.begin()+n+1 );
std::pop_heap( v.begin(), v.end() );
v.pop_back();
Operation is O(ln(n))
RE: request for proof
First, a qualifier:
This method assumes something about the algorithm used by std push_heap.
Specifically, it assumes that std push_heap( v.begin(), v.begin()+n+1 )
will only alter the range [0, n]
for those elements which are ascendants of n, i.e., indices in the following set:
A(n)={n,(n-1)/2,((n-1)/2-1)/2....0}.
Here is a typical spec for std push_heap:
http://www.cplusplus.com/reference/algorithm/push_heap/ "Given a heap range [first,last-1), this function extends the range considered a heap to [first,last) by placing the value in (last-1) into its corresponding location in it."
Does it guarantee to use the "normal heap algorithm" that you read about in textbooks? You tell me.
Anyway, here is the code which you can run and see, empirically, that it works. I am using VC 2005.
#include <algorithm>
#include <vector>
#include <iostream>
bool is_heap_valid(const std::vector<int> &vin)
{
std::vector<int> v = vin;
std::make_heap(v.begin(), v.end());
return std::equal(vin.begin(), vin.end(), v.begin());
}
int _tmain(int argc, _TCHAR* argv[])
{
srand(0);
std::vector<int> v;
for (int i=0; i<100; i++)
{
v.push_back( rand() % 0x7fff );
}
std::make_heap(v.begin(), v.end());
bool bfail = false;
while( v.size() >= 2)
{
int n = v.size()/2;
v[n] = 0x7fffffff;
std::push_heap(v.begin(), v.begin()+n+1);
std::pop_heap(v.begin(), v.end());
v.resize(v.size()-1);
if (!is_heap_valid(v))
{
std::cout << "heap is not valid" << std::endl;
bfail = true;
break;
}
}
if (!bfail)
std::cout << "success" << std::endl;
return 0;
}
But I have another problem, which is how to know the index "n" which needs to be deleted. I cannot see how to keep track of that (know the place in the heap) while using std push_heap and std pop_heap. I think you need to write your own heap code and write the index in the heap to an object every time the object is moved in the heap. Sigh.
Unfortunately, the standard is missing this (fairly important) function. With g++, you can use the non-standard function std::__adjust_heap
to do this, but there's no easy portable way of doing it -- and __adjust_heap
is slightly different in different versions of g++, so you can't even do it portably over g++ versions.
It seems to me that removing from the middle of a heap might mean the entire heap has to be rebuilt: The reason there's no repair_heap is because it would have to do the same (big-oh) work as make_heap
.
Are you able to do something like put std::pair<bool, Item>
in the heap and just invalidate items instead of removing them? Then when they finally get to the top just ignore the item and move along.
How does your repair_heap()
work? Here's my guess:
If your heap is defined by some iterator range, say (heapBegin, heapEnd). The element you want to remove is the root of some subtree of the heap, which is defined by some subrange (subHeapBegin, subHeapEnd). Use std::pop_heap(subHeapBegin, subHeapEnd)
, then if subHeapEnd != heapEnd
, swap the values at *(subHeapEnd-1)
and *(heapEnd-1)
, which should put your deleted item at the end of the heap container. Now you have to percolate the element at *(subHeapEnd-1) up in your subheap. If I haven't missed something, which is possible, then all that remains is to chop the end element off of the heap container.
Before going to the trouble of trying to code that correctly (I've skipped some details like calculating subHeapBegin and subHeapEnd), I'd run some tests to determine if make_heap()
really slows you down. Big-O is useful, but it's not the same thing as actual execution time.
Here's a bit of delphi code i used to remove items from a heap. I don't know this C++ of which you speak and don't have a repair function, but hey..
first the pop, so you get an idea of how the thing works:
function THeap.Pop: HeapItem;
begin
if fNextIndex > 1 then begin
Dec(fNextIndex);
Result:= fBuckets[1]; //no zero element
fBuckets[1] := fBuckets[fNextIndex];
fBuckets[fNextIndex] := nil;
FixHeapDown; //this has a param defaulting to
end
else
Result:= nil;
end;
now to contrast, the deletion:
procedure THeap.Delete(Item: HeapItem);
var
i:integer;
begin
for i:=1 to pred(fNextIndex) do
if Item=fBuckets[i] then begin
dec(fNextIndex);
fBuckets[i] := fBuckets[fNextIndex];
fBuckets[fNextIndex] := nil;
FixHeapDown(i);
break;
end;
end;
its of course a no-no to even think about doing what we're doing here, but hey, costs do change sometimes and jobs do get canceled.
enjoy. i hope this helps.