根在 arr[0] 的二叉堆有啥好处

Posted

技术标签:

【中文标题】根在 arr[0] 的二叉堆有啥好处【英文标题】:What are the benefits of a binary heap with root at arr[0]根在 arr[0] 的二叉堆有什么好处 【发布时间】:2018-09-22 20:31:54 【问题描述】:

我正在通过数组 arr 编写二进制堆。

除了叶子节点之外的每个节点都有两个孩子。

根可以在arr[0]arr[1]

Why in a heap implemented by array the index 0 is left unused? 接受的答案是 arr[1] 更快。

但该答案下方的一条评论说,大多数实现都将 root 设置为 arr[0]

将根放在arr[0] 有什么好处?

【问题讨论】:

节省空间?坦率地说,您发布的链接表明任何性能差异都会很小,这似乎回答了您的问题。 呃。节省 1 个节点的空间以使每个操作都变慢一点?如果是这样的话,我想我会把根放在arr[1] 如果你想从1开始索引,去FORTRAN写代码,如果你想写正确的C,数组索引从0开始。 @DavidC.Rankin 有趣的是,在那些从 1 开始的语言中,生成的代码是从 0 开始的。编译器会为你做索引偏移。 我想人类从1, 2, .. 给打孔卡编号比给它们编号0, 1, ... 更容易,因此该语言的创建者也将其应用于索引。回想一下,自从工程师在衬衫口袋里装计算尺以来,FORTRAN 就已经存在了…… 【参考方案1】:

我是回答您所链接问题的人。

在具有从 0 开始的数组的语言中创建根位于 arr[1] 的二进制堆是愚蠢的。不是因为它浪费了微不足道的空间,而是因为它无益地创建了不必要的混乱代码。

为什么代码令人困惑?因为它打破了我们程序员多年来一直遵循的基本规则:数组从 0 开始。如果你想要一个包含 100 项的数组,你可以这样声明:

int a[100];

二进制堆除外。因为一些在 1973 年将原始二进制堆代码从 Algol(其数组基于 1)转换为 C(基于 0 的数组)的白痴没有大脑来更改子和父计算,所以我们结束了在这种特殊情况下,您必须分配 101 个物品来存放 100 个物品:

int a[101];

当有人打电话给那个人时,他提出了一个似是而非的表现论点。

是的,计算左子索引的代码中有一个额外的递增指令,计算子父索引时还有一个额外的递减指令。在二叉堆所做的更广泛的背景下,这两条指令不会对使用堆的任何程序的运行时间产生实际影响。没有。如果差异甚至可以测量,那肯定会很吵。您的计算机上还会发生许多其他事情,这些事情会对您的程序性能产生更大的影响。

如果您正在编写一个需要高性能优先级队列的程序,那么您首先要使用二进制堆做什么?如果你真的要在优先级队列中存储大量的东西,你可能应该使用像配对堆这样的东西,它会比二叉堆更好,尽管内存成本更高。

C++ STL priority_queue、Java PriorityQueue 和 python 的 heapq 都使用从 0 开始的二进制堆。编写这些包的人了解性能方面的考虑。如果使用基于 1 的二进制堆可以显着提高性能,他们就会这样做。他们使用基于 0 的堆应该告诉您,从基于 1 的堆获得的任何性能提升都是虚幻的。

有关更完整的讨论,请参阅http://blog.mischel.com/2016/09/19/but-thats-the-way-weve-always-done-it/。

【讨论】:

我认为这很好地捕捉到了它。懒惰的程序员从 Algol 或 FORTRAN 等早期语言中提取了许多算法,只是简单地复制和实现了代码,而没有任何预先考虑在 C 中正确索引。“C 中的数字食谱”一书就是一个很好的例子。这足以驱使任何有原则的 C 程序员不得不重写和重新索引所谓的 C 例程。

以上是关于根在 arr[0] 的二叉堆有啥好处的主要内容,如果未能解决你的问题,请参考以下文章

9堆二叉堆

堆之二叉堆

写给自己看的二叉堆:基本操作

算法与数据结构堆排序是什么鬼?

合并果子(二叉堆)

二叉堆和堆排序(binary heap)