1

最近我在这里发布了一个关于stackoverflow多图时间复杂度问题的问题 我得到了一些很好的答案,这些答案让我提到了堆的使用,奇怪的是我以前根本没有使用过。我创建了一个使用 minheap 和 maxheap 重写的新程序。它工作得很好,因为它比我为这个问题实施的任何其他程序都要快得多。唯一的问题是它偶尔会抛出一些错误的答案。我回去做了很多调试。我意识到问题出在堆的组织上。它没有像我认为的那样使用 push_heap 和带有比较操作的 pop_heap 进行排序和分发。此外,当我尝试在 Visual Studio 上运行该程序时,我最终会看到很多断言错误被抛出。我尝试在 cplusplus.com 和 cppreference.com 上阅读更多关于堆及其方法的信息。

让我困惑的第一件事是push_heap。我理解它的方式是这样的:push_heap 有两个参数,默认情况下它将最小值推送到位置 last-1。只有当第一个参数小于第二个参数时它才会这样做,否则它保持不变。它基本上保持了正常堆的顺序。第三个可选参数是一个比较运算符,它可以用作greater(),然后将较大的元素推到最后一个位置。

没有意义的是,如果我在向量中动态插入或删除数字,我会在保持此顺序时遇到问题。如果我希望向量按升序排列,我将使用更大的操作来继续推动堆,以便值升序。但是当您第一次查看 push_heap 方法时会感到困惑,因为它看起来很像其他一些算法函数,它们在一系列数字中执行,例如:

 std::unique (myvector.begin(), myvector.end(), myfunction); 

push_heap 没有。它不会对向量范围内的所有数字进行这种比较操作,这点我一开始并不理解。

在发现 push_heap 并没有真正保持我的向量排序后,我必须保持我的向量排序才能使用二进制搜索。我使用了 sort_heap,但这会减慢程序速度,使其速度不够快。

另外,我发现有时 push_heap 在奇怪的情况下会抛出一个无效的堆错误。

例如:

   push_heap(v.begin(), v.end(), greater<int>());  

向量为755, 98, 55, 22

你会在 push_heap 之后看到:

     22, 98, 55, 755

但是假设你有 22, 98, 55,755

由于比较的错误回报,通常它会继续前进而不执行任何推送。这是可以预料的。

但有时我会尝试 push_heap:

887、52、44、22

它会说

      'invalid heap' 

或者如果我尝试: 22, 52, 44, 887,而不是仅仅返回 false 并继续前进,它将与

'invalid heap'

pop_heap 有时也会发生这种情况。

为什么我得到无效堆?是因为所有堆都必须按降序排列吗?

编辑:我在 cplusplus.com 上找到了这个,我猜它回答了一个问题:

The element with the highest value is always pointed by first. The order of the other elements depends on the particular implementation, but it is consistent throughout all heap-related functions of this header.

4

1 回答 1

4

... push_heap 有两个参数,默认情况下它将最小值推送到位置 last-1。只有当第一个参数小于第二个参数时它才会这样做,否则它保持不变。

没有。如果您的存储是一个向量v,并且当前是一个堆(与创建的一样make_heap),您应该调用

v.push_back(new_item);
push_heap(v.begin(), v.end());

添加新项目。例如,请参见此处此处

考虑一下,它push_heap确实采用了范围[begin, end-1)(已经满足堆不变量所必需的)和附加元素end-1(可能不是),并向上移动最后一个元素,直到为所有[begin, end). 该算法在这里解释。


在发现 push_heap 并没有真正保持我的向量排序后......

未排序。它们有一个排序约束(堆属性),它比完全排序的要弱。

如果要执行二进制搜索,则需要一个完全排序的容器,并且sort_heap每次将堆转换为一个使用既慢又具有破坏性:调用此容器后,容器不再是堆,并且可以不要把它当作一个。


现在,关于您的编辑:堆不必按降序排列。最大堆按降序排列(最大的元素在前面),最小堆按升序排列(最小的元素在前面)。

标准库中的默认设置是构建一个最小堆,operator<用于比较。相反,要构建一个最大堆,您只需传递std::greater<int>(可选)最终参数或其他任何内容。

于 2013-03-25T17:51:37.923 回答