当前位置: 首页 > 知识库问答 >
问题:

std::priority_queue和make_heap API设计

陶宜民
2023-03-14

我正在阅读priority_queue(基本上是make_heap的包装器)的文档,它发现可以用比较函数定制它。

来自文档(http://en . CP preference . com/w/CPP/container/priority _ queue):

可以提供用户提供的Compare来更改排序,例如使用std::更大会导致最小的元素显示为top()。

在维基百科和其他CS文本中,堆被定义为这样的:

在计算机科学中,堆是一种特殊的基于树的数据结构,它满足堆属性:如果A是B的父节点,那么节点A的键(值)相对于节点B的键排序,并且在整个堆中应用相同的排序。堆可以进一步分为“最大堆”或“最小堆”。在最大堆中,父节点的键总是大于或等于子节点的键,最高键在根节点中。

但是在std::priority_queue实现中,提供std::更大会导致创建一个MinHeap(顶部的最小元素)。如果(父比较子级)为真,我会期望最大堆,因为堆顺序是定义的(在我读过的所有文献中)。

我发现这种API设计令人困惑。

有理由这样定义它吗?

共有2个答案

姚俊材
2023-03-14

这是需要考虑的事情——提供< code>std::greater实际上会产生min heap。

这样做的原因如下:从堆的角度来看,比较器的定义小于堆上的关系,即使它实际上是在做其他事情。当std::priority_queue需要插入元素时,它会调用比较器并给它两个元素。如果比较器返回true,它会认为第一个元素小于第二个元素,并将第二个元素放在首位(因为std::priority_queue是最大堆实现)。因此,您最终得到了最小堆。

纪晨
2023-03-14

我也发现它令人困惑,但从一个有点不同的角度来看。假设您要按升序对序列进行排序。有几种方法可以做到这一点:

    < li >将数据放入< code>std::vector或< code > STD::dequee 中,并使用< code>std::less运行< code>std::sort()。 < li >将数据放入< code>std::list中,并使用< code>std::less运行< code>std::list::sort()。 < li >将您的数据插入用< code>std::less配置的< code>std::set中,最后它会自动排序。 < li >将数据放入< code>std::vector或< code > STD::dequee 中,运行< code>std::make_heap(),然后使用< code>std::less运行< code>std::pop_heap()-s。 < li >使用< code>std::greater (!!!).

如我们所见,std::priority_queue从这样的角度来看是一个明确的异常值。

实际上,std::priority_queue在这方面令人困惑的行为背后的原因隐藏在第(4)项中,因为这就是std::priority_queue在下面所做的事情。(4)也违背了我的直觉(尽管在较小程度上),因为在中间状态下(虽然不是所有的std::pop_heap都已执行)序列的排序部分在其上范围,而不是下范围。

但它也解释了为什么为标准库选择了最大堆-<code>std::pop_。

 类似资料:
  • 为什么 首先返回最大的元素(即是最大优先级队列),即使它使用 作为比较类型? 当我想创建一个最小队列时,这尤其让我感到困惑,这将由 是您的最大值。如果您使用创建优先级队列,那么前面是最小的值。我意识到优先级队列使用堆,但我觉得这是有充分理由的。

  • 我想知道为什么使用创建min堆时,应该使用? 对我来说,因为最小的值总是位于堆的顶部,所以使用的类应该是 更新:另一方面,由于(max heap)的默认行为是在顶部保留最大值,因此在我看来,应该用于最大堆创建,而不是最小堆创建

  • 我一直想知道为什么STL优先级队列默认使用最大堆而不是最小堆。我想到的两个明显的用例是寻路(Dijkstra)和构建霍夫曼代码。这两种算法都需要首先拉取最小元素。由于排序(std::sort)默认使用升序,我想知道priority_queue背后的设计原因是什么,因为我非常喜欢默认的最小堆。

  • 并行开发挺复杂的,特别是在试图用好线程和锁的过程中。如果要用到条件变量或std-atomics(一种无锁开发方式),那就更复杂了。C++0x提供了future和promise来简化任务线程间的返回值操作;同时为启动任务线程提供了packaged_task以方便操作。其中的关键点是允许2个任务间使用无(显式)锁的方式进行值传递;标准库帮你高效的做好这些了。基本思路很简单:当一个任务需要向父线程(启动

  • 标准库函数bind()和function()定义于头文件<functional>中(该头文件还包括许多其他函数对象),用于处理函数及函数参数。bind()接受一个函数(或者函数对象,或者任何你可以通过”(…)”符号调用的事物),生成一个其有某一个或多个函数参数被“绑定”或重新组织的函数对象。(译注:顾名思义,bind()函数的意义就像它的函数名一样,是用来绑定函数调用的某些参数的。)例如: int

  • 我在理解条件变量及其在互斥体中的使用时遇到了一些困难,我希望社区能帮助我。请注意,我来自win32背景,因此与CRITICAL_SECTION、HANDLE、SetEvent、WaitForMultipleObject等一起使用。 这是我第一次尝试使用C++11标准库进行并发操作,它是在这里找到的一个程序示例的修改版本。 关于这个的几个问题。 我读过“任何要等待std::condition_var