使用C ++标准库

时间:2018-06-03 15:39:46

标签: c++ algorithm data-structures stl heap

我有一个使用std::make_heap的堆:

std::vector<int> v{1,2,3,5,9,20,3};
std::make_heap(v.begin(), v.end());

现在我通过更改一个随机元素来更新堆:

v[3] = 35;

标准库中是否有办法在O(log n) n时间内再次调整堆,其中std::make_heap是容器的大小。基本上我在寻找heapify函数。我知道哪个元素已被更改。

我了解O(n log n)O(log n)时间。我也经历了重复的问题但是在改变最大元素的意义上是不同的。对于该解决方案,已经在该问题中给出了$text = preg_replace("/(\R){2,}/", "$1", $text); 复杂性。

我正在尝试更改堆中的任何随机元素。

4 个答案:

答案 0 :(得分:4)

如果我们仔细观察你的陈述:

  

现在我通过更改堆的一个随机元素来打扰堆。

对于O(log n)中的堆积,您只能直接&#34;打扰&#34;向量的 back front (以某种方式对应于插入或删除元素)。在这些情况下,可以通过std::push_heapstd::pop_heap算法实现(重新)堆积,这些算法采用对数运行时间。

那就是后面:

v.back() = 35;
std::push_heap(v.begin(), v.end()); // heapify in O(log n)

或前方:

v.front() = 35;

// places the front at the back
std::pop_heap(v.begin(), v.end()); // O(log n)
// v.back() is now 35, but it does not belong to the heap anymore

// make the back belong to the heap again
std::push_heap(v.begin(), v.end()); // O(log n)

否则,您需要使用std::make_heap重新定义整个矢量,这需要线性运行时间。

摘要

无法修改堆的任意元素并使用标准库(即函数模板std::push_heapstd::pop_heap)在对数运行时实现堆积。但是,您始终可以自己实现堆的 swim sink 操作,以便在对数运行时进行堆积。

答案 1 :(得分:3)

你可以自己做:

void modify_heap_element(std::vector<int> &heap, size_t index, int value)
{
    //while value is too large for its position, bubble up
    while(index > 0 && heap[(index-1)>>1] < value)
    {
        size_t parent = (index-1)>>1;
        heap[index]=heap[parent];
        index = parent;
    }
    //while value is too large for its position sift down
    for (;;)
    {
        size_t left=index*2+1;
        size_t right=left+1;
        if (left >= heap.size())
            break;
        size_t bigchild = (right >= heap.size() || heap[right] < heap[left] ?
                           left : right );
        if (!(value < heap[bigchild]))
           break;
        heap[index]=heap[bigchild];
        index = bigchild;
    }
    heap[index] = value;
}

答案 2 :(得分:1)

我一直面临着想要“可更新堆”的问题。但是,最后,我没有编写自定义可更新堆或类似的东西,而是以不同的方式解决了它。

要保持对最佳元素的访问而不需要显式遍历堆,您可以使用要订购的元素的版本化包装。每个唯一的true元素都有一个版本计数器,每次元素更改时都会增加。然后堆中的每个包装器都带有一个元素版本,它是创建包装器时的版本:

struct HeapElemWrapper
{
    HeapElem * e;

    size_t version;        
    double priority;

    HeapElemWrapper(HeapElem * elem)
     : e(elem), version(elem->currentVersion), priority(0.0)
    {}

    bool upToDate() const
    {
        return version == e->currentVersion;
    }

    // operator for ordering with heap / priority queue:
    // smaller error -> higher priority
    bool operator<(const HeapElemWrapper & other) const
    {
        return this->priority> other.priority;
    }
};

当从堆中弹出最顶层的元素时,您可以简单地检查此包装元素以查看它是否与原始元素保持同步。如果没有,只需将其丢弃并弹出下一个。这种方法非常有效,我也可以在其他应用程序中看到它。你唯一需要注意的是你不时地通过堆来清理过时的元素(比如每1000次插入)。

答案 3 :(得分:0)

It's not possible可以仅使用标准库提供的功能模板std::pop_heap()std::push_heap()在对数运行时修改堆中的任意元素,而不会违反堆属性。

但是,您可以为此定义自己的类似STL的功能模板set_heap_element()

template<typename RandomIt, typename T, typename Cmp>
void set_heap_element(RandomIt first, RandomIt last, RandomIt pos, T value, Cmp cmp)
{
    const auto n = last - first;
    *pos = std::move(value); // replace previous value

    auto i = pos - first;
    using std::swap;

    // percolate up
    while (i > 0) { // non-root node
        auto parent_it = first + (i-1)/2;

        if (cmp(*pos, *parent_it))
            break; // parent node satisfies the heap-property 

        swap(*pos, *parent_it); // swap with parent
        pos = parent_it;
        i = pos - first;
    }

    // percolate down
    while (2*i + 1 < n) { // non-leaf node, since it has a left child
        const auto lidx = 2*i + 1, ridx = 2*i + 2;

        auto lchild_it = first + lidx; 
        auto rchild_it = ridx < n? first + ridx: last;

        auto it = pos;
        if (cmp(*it, *lchild_it))
            it = lchild_it;
        if (rchild_it != last && cmp(*it, *rchild_it))
            it = rchild_it;

        if (pos == it)
            break; // node satisfies the heap-property

        swap(*pos, *it); // swap with child
        pos = it;
        i = pos - first;
    }
}

然后,您可以为 max堆提供以下简化的set_heap_element()重载:

#include <functional> // std::less

template<typename RandomIt, typename T>
void set_heap_element(RandomIt first, RandomIt last, RandomIt pos, T value) {
    return set_heap_element(first, last, pos, value, std::less<T>{});
}

此重载使用std::less<T>对象作为原始功能模板的比较功能对象。


示例

在您的 max-heap 示例中,set_heap_element()的用法如下:

std::vector<int> v{1,2,3,5,9,20,3};
std::make_heap(v.begin(), v.end());

// set 4th element to 35 in O(log n)
set_heap_element(v.begin(), v.end(), v.begin() + 3, 35);

只要在设置了元素后,要检查std::is_heap()是否仍然满足 max-heap属性,您可以使用v,它花费线性时间。上方的set_heap_element()功能模板:

assert(std::is_heap(v.begin(), v.end())); 

min 堆如何?

对于 min堆,您可以通过传递std::greater<int>对象作为函数调用的最后一个参数std::make_heap()set_heap_element()和{ {1}}:

std::is_heap()