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

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

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

我错过了什么?


当前回答

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

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

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


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

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

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

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

正如您所看到的,并不是所有的heapify操作都是O(logn),这就是为什么您会得到O(n)。

其他回答

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

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

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


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

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

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

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

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

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

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

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