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

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

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

我错过了什么?


当前回答

基本上,在构建堆时,只在非叶节点上完成工作。。。所做的工作是减少交换量以满足堆条件。。。换句话说(在最坏的情况下),数量与节点的高度成比例。。。总之,问题的复杂性与所有非叶节点的高度之和成正比。。即(2^h+1-1)-h-1=n--1=O(n)

其他回答

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

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

在向堆中插入n个对象的情况下,第i次插入的复杂性为O(logn_i),其中n_i是插入i时堆的大小。只有最后一次插入的复杂度为O(log 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)。

我真的很喜欢杰里米·韦斯特的解释。。。。这里给出了另一种非常容易理解的方法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)。

我们知道堆的高度是log(n),其中n是元素的总数当我们执行heapify操作时,最后一级(h)的元素甚至不会移动一步。第二个最后一级(h-1)的元素数为2h-1,它们最多可以移动1级(在堆化期间)。类似地,对于第i层,我们有2i个元素可以移动h-i个位置。

因此,移动总数:

S=2h*0+2h-1*1+2h-2*2+。。。20*小时

S=2h{1/2+2/22+3/23+…h/2h}-------------------------------------------------1

这是AGP系列,用于解决两边除以2的问题S/2=2h{1/22+2/23+…h/2h+1}-------------------------------------------------2

从1中减去方程式2得到S/2=2h{1/2+1/22+1/23+…+1/2h+h/2h+1}S=2h+1{1/2+1/22+1/23+…+1/2h+h/2h+1}

现在1/2+1/22+1/23++1/2h是减小GP,其和小于1(当h趋于无穷大时,和趋于1)。在进一步的分析中,让我们对和取一个上限,即1。

这给出了:S=2h+1{1+h/2h+1}=2h+1+h~2h+h

h=对数(n),2h=n因此S=n+log(n)T(C)=O(n)

简短回答

使用Heapify()构建二进制堆需要O(n)时间。

当我们一个接一个地将元素添加到堆中,并在每一步都满足堆属性(最大堆或最小堆)时,总时间复杂度将为O(nlogn)。因为二进制堆的一般结构是一个完整的二进制树。因此,堆的高度为h=O(logn)。因此,元素在堆中的插入时间等于树的高度,即O(h)=O(logn)。对于n个元素,这将花费O(nlogn)时间。

现在考虑另一种方法。为了简单起见,我假设我们有一个最小堆。因此,每个节点都应该小于其子节点。

在完整的二叉树的骨架中添加所有元素。这需要O(n)时间。现在我们只需要满足min堆属性。由于所有叶元素都没有子元素,因此它们已经满足堆属性。叶元素的总数是ceil(n/2),其中n是树中存在的元素的总数。现在,对于每个内部节点,如果它大于其子节点,则以从下到上的方式将其与最小子节点交换。每个内部节点将花费O(1)时间。注意:我们不会像插入时那样将值交换到根。我们只需交换一次,使该节点上的子树成为一个合适的最小堆。在二进制堆的基于数组的实现中,我们有父级(i)=ceil((i-1)/2),i的子级由2*i+1和2*i+2给出。因此,通过观察,我们可以说数组中的最后一个ceil(n/2)元素将是叶节点。深度越大,节点的索引就越多。我们将对阵列[n/2]、阵列[n/2-1]重复步骤4。。。。。数组[0]。通过这种方式,我们确保我们以自下而上的方式完成这项工作。总的来说,我们最终将维护min堆属性。所有n/2元素的步骤4将花费O(n)时间。

因此,使用这种方法进行堆化的总时间复杂度将为O(n)+O(n)~O(n(n)。