从std :: heap的中间删除一个元素

时间:2011-01-19 17:17:24

标签: c++ data-structures stl priority-queue

我正在使用优先级队列作为具有一个额外要求的调度程序。我需要能够取消预定的项目。这相当于从优先级队列的中间删除一个项目。

我无法使用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这样的新函数,其中包含一个大的O< 3N 。我会为它提供已取消项目所在的洞位置,并且可以正确调整堆。

不提供std::repair_heap功能似乎是一个巨大的疏忽。我错过了一些明显的东西吗?

是否有库提供符合stl标准的std::repair_heap

是否有更好的数据结构来建模调度程序?

注意
我出于几个原因没有使用std::map

  • 堆具有恒定的内存开销。
  • 堆有很棒的缓存局部性。

6 个答案:

答案 0 :(得分:6)

我猜你知道要删除的堆容器(索引n)中的哪个元素。

  1. 设置值v[n] = BIG;,值BIG确实比堆中的任何其他值大。
  2. 致电std::push_heap( v.begin(), v.begin()+n+1 );
  3. 致电std::pop_heap( v.begin(), v.end() );
  4. 致电v.pop_back();
  5. 完成
  6. 操作是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)中的值放入其中的相应位置,将被视为堆的范围扩展为[first,last]。”

    它是否保证使用您在教科书中阅读的“常规堆算法”? 你告诉我。

    无论如何,这里是你可以运行的代码,从经验上看它是有效的。 我正在使用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时,我无法看到如何跟踪它(知道堆中的位置)。我认为你需要编写自己的堆代码,并在每次在堆中移动对象时将堆中的索引写入对象。叹息。

答案 1 :(得分:4)

不幸的是,标准缺少这个(相当重要的)功能。使用g ++,你可以使用非标准函数std::__adjust_heap来实现这一点,但是没有简单易用的方法 - __adjust_heap在不同版本的g ++中略有不同,所以你可以'甚至可以在g ++版本上轻松实现。

答案 2 :(得分:3)

您的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很有用,但它与实际执行时间不同。

答案 3 :(得分:0)

在我看来,从堆中间删除可能意味着必须重建整个堆:没有repair_heap的原因是因为它必须像{{1}那样做(大哦)工作}。

你是否可以做一些事情,比如把make_heap放在堆中,然后让项目无效而不是删除它们?然后,当他们最终到达顶部时,忽略该项目并继续前进。

答案 4 :(得分:0)

这是我用来从堆中删除项目的一些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;

它当然是一个不禁的想法 做我们在这里做的事情,但嘿,费用 有时会改变,工作也会被取消。

享受。 我希望这会有所帮助。

答案 5 :(得分:0)

您可以尝试将“ std :: multiset”实现为堆结构并支持“ std :: erase”操作,因此可以“ std :: find”该元素然后将其擦除。