如何从std :: heap弹出min元素?

时间:2014-09-17 19:52:38

标签: c++ c++11 vector stl heap

我有std::vector,我在其上使用std::heap

我希望在while循环中,每次循环开始时弹出最小值。在循环体中,另一个元素将插入堆中。经过多次运行后循环停止。

我想到的是使用std::sort_heap然后删除第一个元素。但是,这似乎不是一个好主意。你知道,在我的代码的这一部分,速度是真正的本质。

我认为这不是一个好主意,因为我必须在每次运行时调用std::sort_heap,因为一个元素将要插入,从而使std::vector再次成为堆,这意味着我必须再次对它进行排序,以便将最小元素放在前面。

此外,从开始删除vector.erase()比删除最后一个元素慢,对吗?如果我删除了第一个元素,那么所有其他元素都必须向左移动一个位置。如果我删除了最后一个元素,我只需支付删除费用。

这引出了我的想法,按照我的方式对矢量进行排序,以便最小元素将结束,从而仅删除最后一个元素。但是,这仍然需要在每次循环运行时进行排序,对吗?

解决此问题的最佳方法是什么?

4 个答案:

答案 0 :(得分:7)

使用std::heap排序的std::less可以轻松提取最大的元素,而不是最小的元素。但是,如果使用std::greater,则可以有效地提取min元素。

std::pop_heap,使用std::greater,将最小元素放在序列的后面,并将堆的大小减小一。然后,您可以阅读back()并使用新元素写入back()

std::push_heap,使用std::greater,将元素插入序列的后面,将堆的大小增加一。

在代码中:

#include <algorithm>
#include <vector>
#include <iostream>

int
main()
{
    std::vector<int> v{5, 4, 6, 9, 0, 1, 2, 3};
    std::make_heap(v.begin(), v.end(), std::greater<int>{});
    int i;
    while (std::cin >> i)
    {
        // pop minimum, and place at back()
        std::pop_heap(v.begin(), v.end(), std::greater<int>{});
        // write min out
        std::cout << "min = " << v.back() << '\n';
        // push a new element into the heap
        v.back() = i;
        std::push_heap(v.begin(), v.end(), std::greater<int>{});
    }
}

使用vector这一切都非常有效,因为提取和插入实际上位于vector的后面。因为我们正在使用greater,所以我们实际上每次都会提取最小元素(这有点不直观)。

如果需要,可以将上述逻辑封装在std::priority_queue<T, std::vector<T>, std::greater<T>>中,只需调整vector并公开pushpoptop成员函数vector的{​​{1}},push_backpop_back。此适配器只需在适当的位置拨打backmake_heappush_heap

<强>更新

TemplateRex正确地指出,在C ++ 14 pop_heap中可以使用greater<>来代替greater<int>。例如:

std::make_heap(v.begin(), v.end(), std::greater<>{});

这会创建一个异构仿函数,可以使用任何两个参数,而不仅仅是int

template <>
struct greater<void>
{
    template <class T1, class T2>
    auto operator()(T1&& t, T2&& u) const
        { return std::forward<T1>(t) > std::forward<T2>(u); }
    typedef void is_transparent;
};

使用这个新功能,只需要更改vector以更改类型(以及i的类型),示例的其余部分将自动适应类型的更改。

答案 1 :(得分:3)

我想你只想要一个std::priority_queue<T>。推送和弹出的复杂度为O(log n)

答案 2 :(得分:2)

查看pop_heappush_heap。正如sharth所建议的那样,您也可以使用priority_queue容器适配器,它使用方便的优先级队列接口来封装堆功能。

答案 3 :(得分:2)

std::sort_heap不会创建堆 - 相反,它会将堆作为输入,并生成完全排序的元素作为其输出。

您可以使用priority_queue,如@Sharth所建议的那样。如果您更喜欢直接使用堆,可以使用push_heappop_heap删除最小的项目并分别插入新项目。

请注意,与大多数算法不同,push_heappop_heap只是重新排列范围内的项目。即,pop_heap移动堆中最小(或最大,取决于您的比较函数)项,并将其移动到范围的末尾,并将前面的元素重新排列到有效堆中。

同样地,push_heap在内存的一个部分中占用一堆,然后在内存中的下一个位置中占用一个项目,并将该元素添加到堆中,因此整体形成一个有效的堆。