为什么完全二叉树最适合堆实现?
Why is a complete binary tree most suited for heap implementation?
我想不通为什么完全二叉树最适合实现堆?为什么我们不能使用完整的二叉树?为什么完全二叉树最适合堆实现?
首先,如果不紧密打包,就不可能创建堆结构。数组中的每一项都有二叉树中的一个位置,这个位置来自数组索引
另外,它有几个优点,如下所示:
堆的一些操作具有O(lgn)
的时间复杂度,其中n
是树的高度,将树的高度保持在最小值允许我们将这些操作所需的时间保持在最低限度。
完整二叉树的所有项都以连续的方式存储在数组中,因此通过将堆保持为完整二叉树可以进行随机访问
完整性确保了在元素被移除时有一个well-defined和有效的方法来确定新的根,不使用完整的结构将意味着失去这个优势(这就是为什么你应该首先使用堆)。
堆的完整二叉树?
一个full binary tree不一定是well-balanced。例如,这是一个完整的二叉树:
1
/ \
2 3
/ \
4 5
/ \
6 7
/ \
8 9
/ \
10 11
一般来说,任意右child也是一片叶子的满二叉树,其高度为O(),更准确地说,(−1 )/2。这对于堆来说是有问题的,堆依赖于树的良好平衡以将其 insert/delete 操作保持在 O(log) 的时间复杂度内。
其次,完整的二叉树总是有 奇数 个节点(除非它们为空)。这已经使它们变得不切实际,因为显然堆也应该能够具有 even 大小。
其他选择
但是,二叉堆没有完全二叉树。只有当它们的实现是 well-known array-based 时才需要这样做。但是也可以用 AVL 树实现一个二叉堆,这不一定是一个完整的二叉树,但它仍然保持树的平衡并且对堆操作具有相同的时间复杂度。但是由于指针管理的开销比使用数组中的索引大,数组表示导致更快的操作。
为什么要完成?
当实现是 array-based 而不是显式 node-pointer 表示时,完整二叉树的选择开始发挥作用。当您用 level-order 中的值填充数组并且不允许数组中存在 gaps(未使用的插槽)时,则因此树是完整的。虽然您可以想象一个允许间隙的数组,但这将是一个次等的选择,因为它浪费了 space,没有任何收益来补偿它。
我想不通为什么完全二叉树最适合实现堆?为什么我们不能使用完整的二叉树?为什么完全二叉树最适合堆实现?
首先,如果不紧密打包,就不可能创建堆结构。数组中的每一项都有二叉树中的一个位置,这个位置来自数组索引
另外,它有几个优点,如下所示:
堆的一些操作具有
O(lgn)
的时间复杂度,其中n
是树的高度,将树的高度保持在最小值允许我们将这些操作所需的时间保持在最低限度。完整二叉树的所有项都以连续的方式存储在数组中,因此通过将堆保持为完整二叉树可以进行随机访问
完整性确保了在元素被移除时有一个well-defined和有效的方法来确定新的根,不使用完整的结构将意味着失去这个优势(这就是为什么你应该首先使用堆)。
堆的完整二叉树?
一个full binary tree不一定是well-balanced。例如,这是一个完整的二叉树:
1
/ \
2 3
/ \
4 5
/ \
6 7
/ \
8 9
/ \
10 11
一般来说,任意右child也是一片叶子的满二叉树,其高度为O(),更准确地说,(−1 )/2。这对于堆来说是有问题的,堆依赖于树的良好平衡以将其 insert/delete 操作保持在 O(log) 的时间复杂度内。
其次,完整的二叉树总是有 奇数 个节点(除非它们为空)。这已经使它们变得不切实际,因为显然堆也应该能够具有 even 大小。
其他选择
但是,二叉堆没有完全二叉树。只有当它们的实现是 well-known array-based 时才需要这样做。但是也可以用 AVL 树实现一个二叉堆,这不一定是一个完整的二叉树,但它仍然保持树的平衡并且对堆操作具有相同的时间复杂度。但是由于指针管理的开销比使用数组中的索引大,数组表示导致更快的操作。
为什么要完成?
当实现是 array-based 而不是显式 node-pointer 表示时,完整二叉树的选择开始发挥作用。当您用 level-order 中的值填充数组并且不允许数组中存在 gaps(未使用的插槽)时,则因此树是完整的。虽然您可以想象一个允许间隙的数组,但这将是一个次等的选择,因为它浪费了 space,没有任何收益来补偿它。