我发现其他答案令人困惑,所以我决定用一个实际的示例堆来解释它。
假设原始堆大小为 N 并且您想找到第 k 个最大的元素,
该解决方案需要 O(klogk) 时间和 O(k) 空间。
10
/ \
5 3
/ \ /\
4 1 2 0
Original Heap, N = 7
想要找到第 5 个最大的元素。 k = 5
注意:在新堆中,您需要存储指向原始堆的指针。
这意味着,您不会删除或更改原始堆。原始堆是只读的。因此,您永远不必执行任何需要 O(logN) 时间的操作。
设 x' 是指向原始堆中值 x 的指针。
第一次迭代:获取根节点的指针到新堆中
第 1 步:添加指向节点 10 的指针
10'
New Heap, size = 1, root = 10', root->left = 5, root right->3
打印第一个最大的元素 = 10
第二次迭代:参考原始堆并将其两个子代插入新堆。 (存储指向它们的指针而不是值本身)。您想要存储指针的原因是,您可以稍后在 O(1) 中从原始堆访问它们以搜索它们的子代,而不是 O(N) 来搜索该值在原始堆中的位置。
步骤 2a:从原始堆中寻找新堆根节点的左子节点。
为左孩子(在本例中为 5')添加一个指向新堆的指针。
10'
/
5'
New Heap, size = 2, root = 10', root->left = 5, root right->3
步骤 2b:从原始堆中寻找新堆根节点的右子节点。
将左孩子的指针(在本例中为 3')添加到新堆。
10'
/ \
5' 3'
New Heap, size = 3, root = 10', root->left = 5, root right->3
步骤 2c:从新堆中删除根节点。
(用最右边的叶子交换最大节点,删除根节点并向下冒泡当前根以保持堆属性)
10' swap 3' remove & bubble 5'
/ \ => / \ => /
5' 3' 5' 10' 3'
New Heap, size = 2, root = 5', root->left = 4, root right->1
打印第二大元素 = 5
步骤 3a:从原始堆中寻找新堆根节点的左子节点。
为左孩子(在本例中为 4')添加一个指向新堆的指针。
5'
/ \
3' 4'
New Heap, size = 3, root = 5', root->left = 4, root right->1
步骤 3b:从原始堆中寻找新堆根节点的右子节点。
将左孩子的指针(在本例中为 1')添加到新堆。
5'
/ \
3' 4'
/
1'
New Heap, size = 4, root = 5', root->left = 4, root right->1
步骤 3c:从新堆中删除根节点。
(将新堆的最大节点(5')与其最右边的从新堆的原始堆(1')交换,移除根节点并向下冒泡当前根以保持堆属性)
5' Swap 1' remove & bubble 4'
/ \ => / \ => / \
3' 4' 3' 4' 3' 1'
/ /
1' 5'
New Heap, size = 3, root = 4', root->left = NULL, root right->NULL
打印第三大元素 = 4
步骤 4a 和步骤 4b 什么都不做,因为在这种情况下,根节点没有来自原始堆的任何子节点。
步骤 4c:从新堆中删除根节点。
(将最大节点与最右边的叶子交换,删除根节点并向下冒泡当前根以保持新堆中的堆属性)
4' Swap 1' remove & bubble 3'
/ \ => / \ => /
3' 1' 3' 4' 1'
New Heap, size = 2, root = 3', root->left = 2, root right->0
打印第 4 个最大的元素 = 3
步骤 5a:从原始堆中寻找新堆根节点的左子节点。
为左孩子(在本例中为 2')添加一个指向新堆的指针。
3'
/ \
1' 2'
New Heap, size = 3, root = 3', root->left = 2, root right->0
步骤 5b:从原始堆中寻找新堆根节点的右子节点。
将左孩子的指针(在本例中为 0')添加到新堆。
3'
/ \
1' 2'
/
0'
New Heap, size = 4, root = 3', root->left = 2, root right->0
步骤 5c:从新堆中删除根节点。
(将最大节点(3')与新堆中原始堆(即0')的最右边离开,移除根节点并向下冒泡当前根以保持新堆中的堆属性)
3' Swap 0' Remove & Bubble 2'
/ \ => / \ => / \
1' 2' 1' 2' 1' 0'
/ /
0' 3'
New Heap, size = 3, root = 2', root->left = NULL, root->right = NULL
打印第 5 个最大的元素 = 2
最后,由于我们经历了 k 次迭代,k = 5。我们现在可以从新堆中提取根元素的值。在这种情况下,值为 2。
因此,我们从原始堆中找到了第 k 个最大值。
时间复杂度,T(N,k) = O(klogk)
空间复杂度,S(N,k) = O(k)
希望这会有所帮助!
宋志龙,
多伦多大学。