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

将项插入到堆中是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,我们得到:

现在我们证明构建堆是一个线性操作。

其他回答

O(n)的证明

这个证明并不花哨,而且很简单,我只证明了完全二叉树的情况,结果可以推广到完全二叉。

@bcorso已经证明了复杂性分析的证据。但为了那些还在学习复杂性分析的人,我想补充一下:

您最初错误的基础是对语句含义的误解,“插入堆需要O(logn)时间”。插入到堆中确实是O(logn),但您必须认识到n是插入过程中堆的大小。

在向堆中插入n个对象的情况下,第i次插入的复杂性为O(logn_i),其中n_i是插入i时堆的大小。只有最后一次插入的复杂度为O(log 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,例如“构建堆”。在这种情况下,您基本上从树的底层开始工作,交换父节点和子节点,直到满足堆条件。

我们可以使用另一个最佳解决方案来构建堆,而不是重复插入每个元素。具体如下:

任意将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,我们得到:

现在我们证明构建堆是一个线性操作。