有人能解释一下构建堆的复杂性吗?
将项插入到堆中是O(logn),并且插入被重复n/2次(剩余的是叶子,不能违反堆属性)。所以,我认为这意味着复杂性应该是O(n log n)。
换言之,对于我们“heapify”的每个项目,它有可能必须为堆的每个级别(即logn级别)过滤(即筛选)一次。
我错过了什么?
有人能解释一下构建堆的复杂性吗?
将项插入到堆中是O(logn),并且插入被重复n/2次(剩余的是叶子,不能违反堆属性)。所以,我认为这意味着复杂性应该是O(n log n)。
换言之,对于我们“heapify”的每个项目,它有可能必须为堆的每个级别(即logn级别)过滤(即筛选)一次。
我错过了什么?
当前回答
直观地:
“复杂性应该是O(nLog n)……对于我们“heapify”的每个项目,它有可能必须为堆的每个级别(即log n级别)过滤一次。”
不完全是。您的逻辑不会产生严格的界限——它会过度估计每个堆的复杂性。如果从下往上构建,插入(heapify)可以比O(log(n))小得多。流程如下:
(步骤1)前n/2个元素位于堆的底行。h=0,因此不需要heapify。
(步骤2)接下来的n/22个元素从底部向上排列在第1行。h=1,将过滤器向下堆1级。
(步骤i)接下来的n/2i元素从底部排i。h=i,将过滤器堆成i级。
(步骤log(n))最后一个n/2log2(n)=1元素从底部向上进入行log(n。h=log(n),heapify向下过滤log(n)级别。
注意:在第一步之后,1/2的元素(n/2)已经在堆中,我们甚至不需要调用heapify一次。此外,请注意,实际上只有一个元素,即根元素,会导致完整的log(n)复杂性。
理论上:
构建大小为N的堆的总步骤N可以用数学公式表示。
在高度i处,我们已经显示(上面)将有n/2i+1个元素需要调用heapify,并且我们知道高度i处的heapify是O(i)。这给出了:
最后求和的解可以通过取众所周知的几何级数方程两边的导数来找到:
最后,将x=1/2代入上式得到2。将其代入第一个方程得出:
因此,步骤总数大小为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^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)
@bcorso已经证明了复杂性分析的证据。但为了那些还在学习复杂性分析的人,我想补充一下:
您最初错误的基础是对语句含义的误解,“插入堆需要O(logn)时间”。插入到堆中确实是O(logn),但您必须认识到n是插入过程中堆的大小。
在向堆中插入n个对象的情况下,第i次插入的复杂性为O(logn_i),其中n_i是插入i时堆的大小。只有最后一次插入的复杂度为O(log n)。
基本上,在构建堆时,只在非叶节点上完成工作。。。所做的工作是减少交换量以满足堆条件。。。换句话说(在最坏的情况下),数量与节点的高度成比例。。。总之,问题的复杂性与所有非叶节点的高度之和成正比。。即(2^h+1-1)-h-1=n--1=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)。