我正在实现堆排序,我开始想知道堆的不同实现。当你不需要通过索引访问元素时(比如在堆排序中),使用数组实现堆或者像任何其他链接数据结构那样执行堆的优缺点是什么。
我认为重要的是要考虑节点和指针浪费的内存与数组中空白空间浪费的内存,以及在必须调整数组大小时添加或删除元素所花费的时间。
什么时候应该使用每个?为什么?
答案 0 :(得分:1)
就空间而言,如果您知道提前有多少进入堆中,那么使用数组的问题就很少了 - 堆中的值总是可以指向更大的结构。这可能会在堆本身上提供更好的缓存本地化,但是您仍然需要在内存中找到额外的数据。理想情况下,如果您的比较是基于一小部分数据(通常只是一个4字节的浮点数或整数),您可以将其存储为具有指向完整数据的指针的键,并实现良好的缓存一致性。
但是,遍历堆结构本身的堆缓存命中已经不是特别好了。对于完全适合L1 / L2缓存的小堆,它并不是那么糟糕。但是,当你开始击中主内存时,性能会下潜炸弹。通常这不是问题,但如果是,合并排序是你的救星。
当你想要一堆未确定的大小时,会出现更大的问题。但是,即使使用数组,这仍然不是那么糟糕。在非嵌入式环境中,使用漂亮,漂亮的内存系统增加一个带有一些调用的数组(例如realloc,请原谅我的C背景)真的不是那么慢,因为数据可能不需要在内存中物理移动 - 只是一些地址指针魔术大部分。此外,如果您使用数组大小倍增策略(数组太小,重新分配调用的大小加倍),您仍然会得到O(n)摊销成本,且reallocs相对较少且最多双重浪费空间 - 但是嘿,如果您使用的是32位密钥和32位指针,那么无论如何都可以使用链表。
因此,简而言之,我会坚持使用较小的基础数据结构的数组。当堆消失时,我不再需要一次释放的指针。但是,在我看来,更容易阅读基于指针的堆代码,因为处理索引魔法并不是那么简单。如果不考虑性能和记忆,我建议心跳加速。