有人能解释一下构建堆的复杂性吗?

将项插入到堆中是O(logn),并且插入被重复n/2次(剩余的是叶子,不能违反堆属性)。所以,我认为这意味着复杂性应该是O(n log n)。

换言之,对于我们“heapify”的每个项目,它有可能必须为堆的每个级别(即logn级别)过滤(即筛选)一次。

我错过了什么?


当前回答

“构建堆的线性时间界限可以通过计算堆中所有节点的高度之和来显示,这是虚线的最大数量。对于包含N=2^(h+1)–1个节点的高度为h的完美二叉树,节点高度之和为N–h–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)

如果通过重复插入元素来构建堆,那么它将是O(n log n)。然而,通过以任意顺序插入元素,然后应用算法将它们“堆”成正确的顺序(当然取决于堆的类型),可以更有效地创建新的堆。

看见http://en.wikipedia.org/wiki/Binary_heap,例如“构建堆”。在这种情况下,您基本上从树的底层开始工作,交换父节点和子节点,直到满足堆条件。

你的分析是正确的。然而,它并不紧密。

要解释为什么构建堆是一个线性操作并不容易,您应该更好地阅读它。

这里可以看到对算法的详细分析。


主要思想是,在build_heap算法中,所有元素的实际堆化成本不是O(logn)。

当调用heapify时,运行时间取决于进程终止之前元素在树中向下移动的距离。换句话说,它取决于堆中元素的高度。在最坏的情况下,元素可能会一直下降到叶级别。

让我们一级一级地计算完成的工作。

在最底层,有2^(h)个节点,但我们没有对这些节点调用heapify,因此功为0。在下一级有2^(h−1)个节点,每个节点可能向下移动一级。在从底部开始的第3层,有2^(h−2)个节点,每个节点可能向下移动2层。

正如您所看到的,并不是所有的heapify操作都是O(logn),这就是为什么您会得到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)。

直观地:

“复杂性应该是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)