我有std::vector
,我在其上使用std::heap
。
我希望在while循环中,每次循环开始时弹出最小值。在循环体中,另一个元素将插入堆中。经过多次运行后循环停止。
我想到的是使用std::sort_heap
然后删除第一个元素。但是,这似乎不是一个好主意。你知道,在我的代码的这一部分,速度是真正的本质。
我认为这不是一个好主意,因为我必须在每次运行时调用std::sort_heap
,因为一个元素将要插入,从而使std::vector
再次成为堆,这意味着我必须再次对它进行排序,以便将最小元素放在前面。
此外,从开始删除vector.erase()
比删除最后一个元素慢,对吗?如果我删除了第一个元素,那么所有其他元素都必须向左移动一个位置。如果我删除了最后一个元素,我只需支付删除费用。
这引出了我的想法,按照我的方式对矢量进行排序,以便最小元素将结束,从而仅删除最后一个元素。但是,这仍然需要在每次循环运行时进行排序,对吗?
解决此问题的最佳方法是什么?
答案 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
并公开push
,pop
和top
成员函数vector
的{{1}},push_back
和pop_back
。此适配器只需在适当的位置拨打back
,make_heap
和push_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_heap和push_heap。正如sharth所建议的那样,您也可以使用priority_queue容器适配器,它使用方便的优先级队列接口来封装堆功能。
答案 3 :(得分:2)
std::sort_heap
不会创建堆 - 相反,它会将堆作为输入,并生成完全排序的元素作为其输出。
您可以使用priority_queue
,如@Sharth所建议的那样。如果您更喜欢直接使用堆,可以使用push_heap
和pop_heap
删除最小的项目并分别插入新项目。
请注意,与大多数算法不同,push_heap
和pop_heap
只是重新排列范围内的项目。即,pop_heap
移动堆中最小(或最大,取决于您的比较函数)项,并将其移动到范围的末尾,并将前面的元素重新排列到有效堆中。
同样地,push_heap
在内存的一个部分中占用一堆,然后在内存中的下一个位置中占用一个项目,并将该元素添加到堆中,因此整体形成一个有效的堆。