【问题标题】:How to pop min element from std::heap?如何从std :: heap中弹出最小元素?
【发布时间】:2014-11-11 23:58:43
【问题描述】:

我有一个std::vector,我在上面使用std::heap

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

我想到的是使用std::sort_heap,然后删除第一个元素。然而,这似乎不是一个好主意。你看,在我的这部分代码中,速度是非常重要的。

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

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

这使我想到了这个想法,按照我的方式对向量进行排序,以便最小元素到达末尾,从而只删除最后一个元素。但是,这仍然需要在每次循环运行时进行排序,对吧?

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

【问题讨论】:

  • 您是否正在尝试维护最大堆,但具有快速删除最小元素的附加功能?还是您只是想要一个最小堆?
  • 显然我杀得太多了!

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


【解决方案1】:

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

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&lt;T, std::vector&lt;T&gt;, std::greater&lt;T&gt;&gt; 中,它简单地适应vector 并公开pushpoptop 成员函数,而不是vectorpush_backpop_backback。此适配器将在适当的位置简单地调用make_heappush_heappop_heap

更新

TemplateRex 正确指出在 C++14 中greater&lt;&gt; 可以用来代替greater&lt;int&gt;。例如:

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 的类型),示例的其余部分将自动适应类型的更改。

【讨论】:

  • 或在 C++14 中:std::greater&lt;&gt;{},更容易重构为其他类型
  • @TemplateRex 您能否更详细地解释您所指的 C++14 功能及其提供的好处?我不熟悉它。
  • @mattnewport this working paperthis Q&A
  • @TemplateRex 谢谢,我听说过异构容器查找,但没有听说过透明比较器,它实际上似乎比最初对我来说似乎比异构查找更有用。
【解决方案2】:

我想你只是想要一个std::priority_queue&lt;T&gt;。它具有O(log n) 的推送和弹出复杂性。

【讨论】:

    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

      std::sort_heap 不创建堆,而是将堆作为输入,并生成完全排序的元素作为输出。

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

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

      同样,push_heap 在一段内存中获取一个堆,然后在内存中的下一个位置获取一项,并将该元素添加到堆中,因此整体再次形成一个有效堆。

      【讨论】:

        猜你喜欢
        • 2011-06-11
        • 2022-01-27
        • 1970-01-01
        • 2012-03-30
        • 1970-01-01
        • 1970-01-01
        • 2021-08-13
        • 2019-04-20
        • 1970-01-01
        相关资源
        最近更新 更多