首页 文章

为什么我们通过堆而不是二进制搜索树进行排序?

提问于
浏览
5

可以在O(n logn)时间内从列表构造堆,因为将元素插入堆需要O(logn)时间并且有n个元素 .

类似地,可以在O(n logn)时间内从列表构造二叉搜索树,因为将元素插入到BST中需要平均登录时间并且存在n个元素 .

从最小到最大遍历堆需要O(n logn)时间(因为我们必须弹出n个元素,并且每个pop需要O(logn)接收器操作) . 从最小到最大遍历BST需要O(n)时间(字面上只是顺序遍历) .

所以,在我看来,构造两个结构需要相同的时间,但BST迭代的速度更快 . 那么,为什么我们使用“Heapsort”代替“BSTsort”呢?

编辑:感谢Tobias和lrlreon的回答!总之,以下是我们使用堆而不是BST进行排序的要点 .

  • 堆的构造实际上可以在O(n)时间内完成,而不是O(nlogn)时间 . 这使堆构造比BST构造更快 .

  • 此外,数组可以很容易地就地转换为堆,因为堆总是完整的二叉树 . BST不能轻易实现为数组,因为BST不能保证是完整的二叉树 . 这意味着BST需要额外的O(n)空间分配来进行排序,而Heaps只需要O(1) .

  • 堆上的所有操作都保证为O(logn)时间 . 除非 balancer ,否则BST可能具有O(n)运算 . 堆积比 balancer BST更容易实施 .

  • 如果需要在创建堆后修改值,则只需应用接收器或游泳操作即可 . 修改BST中的值在概念上更加困难 .

2 回答

  • 1

    如果排序方法包括将元素存储在数据结构中并在以排序方式提取之后,那么,尽管两种方法(heap和bst)具有相同的渐近复杂度O(n log n),但堆往往更快 . 原因是堆始终是一个完美 balancer 的树,其操作始终是O(log n),以确定的方式,而不是平均 . 使用bst,取决于 balancer 的标准,无论使用哪种 balancer 方法,插入和删除往往比堆更多的时间 . 另外,堆通常用存储树的级别遍历的数组实现,而不需要存储任何类型的指针 . 因此,如果您知道元素的数量(通常是这种情况),则堆所需的额外存储量小于用于bst的额外存储量 .

    在对数组进行排序的情况下,有一个非常重要的原因,它更倾向于堆而不是bst:您可以使用相同的数组来存储堆;无需使用额外的内存 .

  • 3

    我可以想象您希望在搜索树上更喜欢(二进制)堆的原因有多种:

    • 构造:通过从最小子树到最大子树自下而上应用heapify操作,实际上可以在O(n)时间内构造二进制堆 .

    • 修改:二进制堆的所有操作都非常简单:

    • 在末尾插入了一个元素?将其筛选直到堆状态保持不变

    • 将最后一个元素交换到开头?将其快速向下移动直到堆条件成立

    • 更改了条目的密钥?根据变化的方向上下移动它

    • 概念简单:由于其隐式数组表示,任何知道基本索引方案(2i 1,2i 2是i的子代)的人都可以实现二进制堆,而不考虑许多困难的特殊情况 .
      如果你在二叉搜索树中查看这些操作,理论上它们也很简单,但必须明确地存储树,例如,使用指针,大多数操作都需要重新 balancer 树以保持O(log n)高度,这需要复杂的旋转(红黑树)或拆分/合并节点(B树)

    • 编辑:存储:正如Irleon所指出的,要存储BST,您还需要更多存储空间,因为除了值本身之外,每个条目至少需要存储两个子指针,这可能是一个很大的存储开销,特别是对于小型 Value 类型 . 同时,堆不需要额外的指针 .

    要回答关于排序的问题:BST需要O(n)时间按顺序遍历,构造过程需要O(n log n)次操作,如前所述,这些操作很多更复杂 .

    同时,Heapsort实际上可以通过在O(n)时间内从输入数组构建最大堆来实现,然后重复交换最大元素以返回并缩小堆 . 您可以将Heapsort视为插入排序,其中包含一个有用的数据结构,可让您在O(log n)时间内找到下一个最大值 .

相关问题