[英]Remove an element from the middle of an std::heap
我使用優先級隊列作為具有一項額外要求的調度程序。 我需要能夠取消預定項目。 這等同於從優先級隊列的中間刪除一個項目。
我不能使用std::priority_queue
因為對 top 以外的任何元素的訪問都受到保護。
我正在嘗試使用algorithm
的堆函數。 但我仍然缺少我需要的部分。 當我從堆中間刪除一個元素時,我希望它有效地重建自己。 C++ 提供了這些堆函數:
std::make_heap
O(3n)std::push_heap
O(lg(n))std::pop_heap
O(2 lg(n)) 我想要一個像std::repair_heap
這樣的新函數, std::repair_heap
有一個 big-O < 3n 。 我會為它提供被取消的項目曾經駐留的孔的位置,它會正確地調整堆。
不提供std::repair_heap
函數似乎是一個巨大的疏忽。 我錯過了一些明顯的東西嗎?
是否有提供符合 stl 標准的std::repair_heap
?
是否有更好的數據結構來建模調度程序?
注意:
我沒有使用std::map
有幾個原因。
我猜您知道要刪除堆容器(索引 n)中的哪個元素。
v[n] = BIG;
值BIG
確實比堆中的任何其他值都大。std::push_heap( v.begin(), v.begin()+n+1 );
std::pop_heap( v.begin(), v.end() );
v.pop_back();
操作是 O(ln(n))
RE:要求提供證明
首先,一個限定符:這個方法假定一些關於 std push_heap 使用的算法。
具體來說,它假設 std push_heap( v.begin(), v.begin()+n+1 ) 只會改變范圍 [0, n]
對於 n 的上升元素,即以下集合中的索引:
A(n)={n,(n-1)/2,((n-1)/2-1)/2....0}.
這是 std push_heap 的典型規范:
http://www.cplusplus.com/reference/algorithm/push_heap/ "給定一個堆范圍 [first,last-1),這個函數通過將值放在 (last) -1) 放入其對應的位置。”
它是否保證使用您在教科書中讀到的“正常堆算法”? 你告訴我。
無論如何,這是您可以運行並根據經驗查看它是否有效的代碼。 我正在使用 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;
}
但是我還有一個問題,就是如何知道需要刪除的索引“n”。 在使用 std push_heap 和 std pop_heap 時,我看不到如何跟蹤它(知道堆中的位置)。 我認為您需要編寫自己的堆代碼,並且每次在堆中移動對象時將堆中的索引寫入對象。 嘆。
不幸的是,標准缺少這個(相當重要的)功能。 使用 g++,您可以使用非標准函數std::__adjust_heap
來執行此操作,但沒有簡單的可移植方法——而且__adjust_heap
在不同版本的 g++ 中略有不同,因此您甚至無法移植超過 g++ 版本。
您的repair_heap()
如何工作的? 這是我的猜測:
如果您的堆由某個迭代器范圍定義,請說 (heapBegin, heapEnd)。 要刪除的元素是堆的某個子樹的根,它由某個子范圍(subHeapBegin、subHeapEnd)定義。 使用std::pop_heap(subHeapBegin, subHeapEnd)
,然后如果subHeapEnd != heapEnd
,交換*(subHeapEnd-1)
和*(heapEnd-1)
,這應該將您刪除的項目放在堆容器的末尾。 現在您必須在您的子堆中向上滲透 *(subHeapEnd-1) 處的元素。 如果我沒有遺漏任何東西(這是可能的),那么剩下的就是從堆容器中切出結束元素。
在嘗試正確編碼之前(我跳過了一些細節,如計算 subHeapBegin 和 subHeapEnd),我會運行一些測試來確定make_heap()
真的會減慢你的速度。 Big-O 很有用,但它與實際執行時間不同。
在我看來,從堆的中間移除可能意味着必須重建整個堆:沒有 repair_heap 的原因是因為它必須執行與make_heap
相同(大哦)的工作。
您是否能夠執行諸如將std::pair<bool, Item>
放入堆中並僅使項目無效而不是刪除它們的操作? 然后,當他們最終到達頂部時,只需忽略該項目並繼續前進。
您可以嘗試“std::multiset”,它作為堆結構實現並支持“std::erase”操作,因此您可以“std::find”該元素然后將其擦除。
這是我用來從堆中刪除項目的一些 delphi 代碼。 我不知道你說的這個 C++ 沒有修復功能,但是嘿..
首先是流行音樂,所以你可以了解它是如何工作的:
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;
現在對比,刪除:
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;
甚至考慮做我們在這里做的事情當然是不可以的,但是嘿,有時成本確實會發生變化並且工作確實會被取消。
請享用。 我希望這會有所幫助。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.