Algorithm 以O(K*log(K))打印给定堆中最大的K个元素?
鉴于以下问题,我不能完全确定我当前的解决方案: 问题: 给定存储在数组Algorithm 以O(K*log(K))打印给定堆中最大的K个元素?,algorithm,search,tree,heap,big-o,Algorithm,Search,Tree,Heap,Big O,鉴于以下问题,我不能完全确定我当前的解决方案: 问题: 给定存储在数组a中的n元素的最大堆,是否可以在O(K*log(K))中打印所有最大的K元素 我的回答: 是的,这是因为搜索元素需要O(log(K)),因此需要这样做 对于K元素需要O(K*log(K))运行时间。事实上,提取max元素太容易了,提取max元素是O(log(N))其中N是堆的大小。和N≠K 我要补充的是,搜索随机元素是O(N),而不是O(Log(N)),但在这种情况下,我们要提取最大值。在大小为N的堆中搜索元素不是O(K)。首
a
中的n
元素的最大堆,是否可以在O(K*log(K))
中打印所有最大的K
元素
我的回答:
是的,这是因为搜索元素需要O(log(K))
,因此需要这样做
对于
K
元素需要O(K*log(K))
运行时间。事实上,提取max元素太容易了,提取max元素是O(log(N))
其中N
是堆的大小。和N≠K
我要补充的是,搜索随机元素是
O(N)
,而不是O(Log(N))
,但在这种情况下,我们要提取最大值。在大小为N的堆中搜索元素不是O(K)。首先,查找一个元素的时间复杂度取决于您试图提取的元素数(即K所代表的元素数)是没有意义的。此外,没有在堆中搜索这样的事情——除非您在O(N)中计算每个元素搜索的标准外观
然而,根据设计,在堆中查找最大的元素是O(1)(我显然假设它是一个最大的堆,因此最大的元素位于堆的顶部),从大小为N的堆中删除最大的元素是O(log(N))(用一个叶元素替换它,并使该叶渗透回堆中)
因此,从堆中提取K个元素并返回未提取元素的堆需要O(K·log(N))时间
如果以非破坏性方式从堆中提取K个元素,会发生什么?您可以通过保持一堆堆来实现这一点(其中堆的值是其最大元素的值)。最初,这个堆只包含一个元素(原始堆)。要提取下一个最大元素,请提取顶部堆,提取其顶部元素(即最大值),然后将两个子堆重新插入到堆中
这会在每次移除时(移除一个,添加两个)将堆的堆增加一个,这意味着它永远不会容纳超过K个元素,因此移除一个,添加两个将需要O(log(K))。重复此操作,您将得到一个实际的O(K·log(K))算法,该算法返回前K个元素,但无法返回未提取元素的堆。这在最大堆中是可能的,因为您只打印树中的元素,而不提取它们 首先确定位于根节点的最大元素。形成一个指向节点的指针,并将其添加到一个空的“最大值”列表中。然后,对于每个
k
值,在循环中执行以下步骤
- 从列表中弹出最大元素,取O(1)
- 打印其值,取O(1)
- 将此最大元素的每个子元素插入列表。插入时保持排序,需要O(日志(列表大小))时间。这个列表的最大大小是分支大小*k,因为我们执行了k次循环。因此,该步骤需要O(log(k))时间
总的来说,运行时间是O(klog(k)),正如所希望的那样。我发现其他答案令人困惑,所以我决定用一个实际的堆示例来解释它。
It is a simple and elegant algorithm to get first k elements of a max heap in k log(k) time.
steps:-
1.construct another max heap name it auxiliary heap
2.add root element of main heap to auxiliary heap
3.pop out the element from auxiliary heap and add it's 2 children to the heap
4.do step 2 and 3 till k elements have been popped out from auxiliary heap. Add the popped element's children to the auxiliary heap.
假设原始堆的大小为N,并且您希望找到第k个最大的元素,
此解决方案需要O(klogk)时间和O(k)空间
想要找到第五大元素。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
5'
/ \
3' 4'
New Heap, size = 3, root = 5', root->left = 4, root right->1
3'
/ \
1' 2'
New Heap, size = 3, root = 3', root->left = 2, root right->0
步骤2b:从原始堆中查找新堆根节点的正确子级。
将左子级(在本例中为3')的指针添加到新堆
10'
/ \
5' 3'
New Heap, size = 3, root = 10', root->left = 5, root right->3
5'
/ \
3' 4'
/
1'
New Heap, size = 4, root = 5', root->left = 4, root right->1
步骤2c:从新堆中删除根节点。
(将最大节点与最右边的左节点交换,删除根节点并向下冒泡当前根节点以维护堆属性)
打印第二大元素=5
步骤3a:从原始堆中查找新堆根节点的左子级。
将左子级(在本例中为4')的指针添加到新堆
10'
/
5'
New Heap, size = 2, root = 10', root->left = 5, root right->3
5'
/ \
3' 4'
New Heap, size = 3, root = 5', root->left = 4, root right->1
3'
/ \
1' 2'
New Heap, size = 3, root = 3', root->left = 2, root right->0
步骤3b:从原始堆中查找新堆根节点的正确子级。
将左子级(在本例中为1')的指针添加到新堆
10'
/ \
5' 3'
New Heap, size = 3, root = 10', root->left = 5, root right->3
5'
/ \
3' 4'
/
1'
New Heap, size = 4, root = 5', root->left = 4, root right->1
步骤3c:从新堆中删除根节点。
(将新堆的最大节点(5')与其原始堆(1')的最右边的剩余节点从新堆交换,移除根节点并向下冒泡当前根以维护堆属性)
打印第三大元素=4
步骤4a和步骤4b不执行任何操作,因为在本例中,根节点没有来自原始堆的任何子节点
步骤4c:从新堆中删除根节点。
(将最大节点与最右边的左节点交换,移除根节点并向下冒泡当前根节点,以在新堆中维护堆属性)
打印第四大元素=3
步骤5a:从原始堆中查找新堆根节点的左子级。
将左子级(在本例中为2')的指针添加到新的he
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