遍历完整的二进制最小堆

时间:2014-03-04 14:26:37

标签: arrays algorithm language-agnostic heap containers

我不确定如何遍历下面的树结构,以便节点始终按升序排列。对数组[9 8 7 6 5 4 3 2 1 0]进行修改会产生数组[0 1 3 2 5 4 7 9 6 8],我认为该对应于此表示:

heap drawing

想要保持数组不变(因为我想稍后进行高效的插入)如何以升序有效地遍历它? (即按此顺序访问节点[0 1 2 3 4 5 6 7 8 9]

3 个答案:

答案 0 :(得分:3)

只需对数组进行排序。之后它仍然是一个小堆,没有其他算法渐进地更有效。

答案 1 :(得分:2)

您无法遍历堆,就像您可以遍历BST一样。如果排序遍历是一项重要的操作,@ Dukeling对于BST是一个更好的选择是对的。但是,您可以使用以下算法,该算法需要额外的O(1)空间。

假设您拥有通常数组形式的堆。

  1. 按排序顺序逐个删除项目,以“访问”它们进行遍历。
  2. 在访问第i个项目之后,将其放回位于n-i的堆数组中,该数组当前未被堆使用(假设从零开始的数组索引)。
  3. 遍历后,反转数组以创建新堆。
  4. 删除项目需要O(n log n)时间。逆转是另一个O(n)。

    如果您不需要一直遍历,可以随时停止并通过运行O(n)heapify操作“修复”阵列。例如,请参阅pseudocode here

答案 2 :(得分:1)

我实际上建议self-balancing binary search tree (BST)在这里:

  

二叉搜索树(BST)...是基于节点的二叉树数据结构,具有以下属性:

     
      
  • 节点的左子树仅包含键小于节点键的节点。
  •   
  • 节点的右子树只包含键大于节点键的节点。
  •   
  • 左右子树每个也必须是二叉搜索树。
  •   
  • 必须没有重复的节点。
  •   

以排序顺序(所谓的有序遍历)遍历BST比使用堆执行BST更简单,更节省空间。

BST将支持O(log n)插入和O(n)遍历。

如果在再次进行遍历之前进行大量插入操作,则在遍历之前将其排序为数组可能更有效 - 插入的相关运行时间为O(1)和O(n log) n)获得排序顺序 - 这个选项变得比使用BST更有效的确切点需要进行基准测试。


为了好奇,这里是你如何按排序顺序遍历堆(如果你知道,你不想只是从堆中删除最小值,直到它为空,这可能是更简单的选择,因为删除最小值是堆的标准操作。)

从堆的属性中,没有什么能阻止一些元素出现在左子树中,右边的元素在左边,后面的元素再次出现,等等 - 这意味着你不能完全完成左子树然后从右边开始 - 你可能需要在内存中保留大量的堆。

主要思想是基于一个元素总是小于其子元素的事实。

基于此,我们可以构建以下算法:

  • 创建一个堆(另一个)
  • 将原始堆的根插入新堆
  • 虽然新堆有元素:
    • 从堆中删除最小值
    • 输出该元素
    • 将原始堆中该元素的子元素(如果有)添加到新堆

这需要O(n log n)时间和O(n)空间(作为参考,BST遍历需要O(log n)空间),更不用说增加的代码复杂性。