有人能解释一下构建堆的复杂性吗?
将项插入到堆中是O(logn),并且插入被重复n/2次(剩余的是叶子,不能违反堆属性)。所以,我认为这意味着复杂性应该是O(n log n)。
换言之,对于我们“heapify”的每个项目,它有可能必须为堆的每个级别(即logn级别)过滤(即筛选)一次。
我错过了什么?
有人能解释一下构建堆的复杂性吗?
将项插入到堆中是O(logn),并且插入被重复n/2次(剩余的是叶子,不能违反堆属性)。所以,我认为这意味着复杂性应该是O(n log n)。
换言之,对于我们“heapify”的每个项目,它有可能必须为堆的每个级别(即logn级别)过滤(即筛选)一次。
我错过了什么?
当前回答
我们可以使用另一个最佳解决方案来构建堆,而不是重复插入每个元素。具体如下:
任意将n个元素放入数组中以尊重堆的形状属性。从最底层开始,向上移动,筛选在heapify down过程中,每个子树向下移动,直到堆属性已还原。
此过程可通过下图进行说明:
接下来,让我们分析一下上述过程的时间复杂性。假设堆中有n个元素,堆的高度为h(对于上图中的堆,高度为3)。那么我们应该有以下关系:
当最后一级只有一个节点时,n=2^h。当树的最后一级被完全填充时,则n=2^(h+1)。
并且从底部开始作为级别0(根节点是级别h),在级别j中,最多有2^(h-j)个节点。每个节点最多执行j次交换操作。所以在第j级中,操作的总数是j*2^(h-j)。
因此,构建堆的总运行时间与:
如果我们将2^h项考虑在内,那么我们得到:
正如我们所知,∑j/2是一个收敛到2的级数(详细地说,你可以参考这个wiki)。
使用此功能,我们可以:
根据条件2^h<=n,我们得到:
现在我们证明构建堆是一个线性操作。
其他回答
在构建堆的情况下,我们从高度开始,logn-1(其中logn是n个元素的树的高度)。对于高度为“h”的每个元素,我们将最大值设置为(logn-h)。
So total number of traversal would be:-
T(n) = sigma((2^(logn-h))*h) where h varies from 1 to logn
T(n) = n((1/2)+(2/4)+(3/8)+.....+(logn/(2^logn)))
T(n) = n*(sigma(x/(2^x))) where x varies from 1 to logn
and according to the [sources][1]
function in the bracket approaches to 2 at infinity.
Hence T(n) ~ O(n)
我真的很喜欢杰里米·韦斯特的解释。。。。这里给出了另一种非常容易理解的方法http://courses.washington.edu/css343/zander/NotesProbs/heapcomplexity
因为,buildheap依赖于使用依赖于heapify,而shiftdown方法依赖于所有节点的高度之和。因此,求出节点高度之和S=(2^i*(h-i))从i=0到i=h的总和,其中h=logn是树的高度求解s,我们得到s=2^(h+1)-1-(h+1)因为,n=2^(h+1)-1s=n-h-1=n-logn-1s=O(n),所以构建堆的复杂度是O(n)。
已经有一些很好的答案,但我想补充一点直观的解释
现在,看看图片,有n/2^1个高度为0的绿色节点(此处23/2=12)n/2^2个高度为1的红色节点(此处23/4=6)n/2^3高度为2的蓝色节点(此处23/8=3)n/2^4个紫色节点,高度为3(此处23/16=2)因此高度h有n/2^(h+1)个节点要计算时间复杂度,可以计算每个节点完成的工作量或执行的最大迭代次数现在可以注意到,每个节点都可以执行(atmost)迭代==节点的高度
Green = n/2^1 * 0 (no iterations since no children)
red = n/2^2 * 1 (heapify will perform atmost one swap for each red node)
blue = n/2^3 * 2 (heapify will perform atmost two swaps for each blue node)
purple = n/2^4 * 3 (heapify will perform atmost three swaps for each purple node)
因此,对于高度为h的任何节点,所做的最大功为n/2^(h+1)*h
现在完成的总工作量为
->(n/2^1 * 0) + (n/2^2 * 1)+ (n/2^3 * 2) + (n/2^4 * 3) +...+ (n/2^(h+1) * h)
-> n * ( 0 + 1/4 + 2/8 + 3/16 +...+ h/2^(h+1) )
现在对于h的任何值,序列
-> ( 0 + 1/4 + 2/8 + 3/16 +...+ h/2^(h+1) )
永远不会超过1因此,构建堆的时间复杂度永远不会超过O(n)
在构建堆时,假设您采用的是自下而上的方法。
您获取每个元素并将其与子元素进行比较,以检查该元素对是否符合堆规则。因此,叶被免费包含在堆中。那是因为他们没有孩子。向上移动,叶子正上方节点的最坏情况是1次比较(最多只能与一代孩子进行比较)再往上看,他们的直系父母最多可以与两代子女相比。继续朝着相同的方向,在最坏的情况下,您将对根进行log(n)比较。并且log(n)-1用于其直系子代,log(n)-2用于其直系子女,依此类推。所以总结起来,你会得到类似log(n)+{log(n(n)-1}*2+{log(n)-2}*4+…..+1*2^{(logn)-1},它只是O(n)。
“构建堆的线性时间界限可以通过计算堆中所有节点的高度之和来显示,这是虚线的最大数量。对于包含N=2^(h+1)–1个节点的高度为h的完美二叉树,节点高度之和为N–h–1。因此它是O(N)。"