我正在给自己一个关于数据结构和算法的进修课程(以及学习新东西 - 我是大学里的信息系统专业,而不是计算机科学,所以我没有接受过这方面的正规教育),而且我一直在努力工作。我有点困惑。
我的理解是Heap基本上是一个半排序的树,其中每个子节点的值保证小于其父节点的值(假设为此讨论MinHeaps)。所以,如果它是一棵树,为什么我看到的每个实现都在内部使用了类似数组的结构而不是构建一组树节点?
我必须记住阵列中N的孩子坐在2N + 1(左)和2N + 2(右)*这对我来说似乎很奇怪。为什么不只是构建一个具有Left和Right属性的节点并从那里开始?
*来源:This article
答案 0 :(得分:4)
TL; DR:节省内存开销,从数据位置获得更快的速度。
对于二进制树,每个节点需要4个字节用于左子节点,4个字节用于右子节点(如果在64位系统上则为8 + 8)。这只是你需要的指针。如果你存储一个32位的int,这是一个很大的开销。为将节点推向根所需的父节点添加另一个指针,并且您正在查看64位系统上4字节整数的24字节开销。
对于堆,您不需要担心任意树。您通常只担心头部(值的最小值/最大值)而您不关心内部结构。堆是一个几乎完整的二叉树(除了从左到右填充的最后一个,所有级别都被填充)。在此结构中,如果您只是将节点放在一个数组中,那么对于索引为x
的节点,您总是会找到(x+1)/2
处的父项x*2+1
处的父项{}}处的父项{ {1}}。所以不需要存储任何这些胖指针。
除了节省的空间之外,你还可以获得速度提升,因为内存是连续的,因此它更有可能被缓存在一起(不保证,更有可能)。
当然,如果不是效率很重要的东西,你可以将它作为常规树来实现。相反,如果你有一个几乎完整的树,并且你想要最大化你的系统,那就用数组实现它(即使你不把它用作堆)。
答案 1 :(得分:1)
首先,让我们对词汇做一点澄清:
add
和deleteMin
,有时decreaseKey
的操作。实际上,你可以创建一个简单的数组/列表来循环遍历结构以找到最小值,并且你将实现一个优先级队列(效率非常低,但仍然如此)。A heap is a tree-based data structure that satisfies the heap property
(Wikipedia)。 heap属性是:父项的键低于其子项。我第一次听说二进制堆时,我也认为在数组中有一棵树是非常奇怪的,而且你必须做一些奇怪的乘法来得到孩子/父。
在你脑海中表现它更加困难,但如果你看得更近一点,那就更有意义了:
getRight(int node)
,getLeft(int node)
和getParent(int node)
等帮助程序,实施将更加熟悉。但是,二进制堆不具有缓存效率,因为子节点离父节点很远,但它可能比基于节点的等效二进制堆更高效。
现在,如果你看一下优点和缺点,唯一的问题是,基于数组的二进制堆需要再多一步才能在一个人的头脑中进行描绘,但它赢得了其他一切。 / p>
我不知道原始堆是否被设计为数组,但不知道有一天有人找到了这个实现,并且数组已经成为二进制堆的标准。
然而,其他类型的堆是用节点实现的,所以它是一个特例。