算法作业 证明堆排序最好时间复杂度是 Ω(nlogn)
证明堆排序最好时间复杂度是 Ω(nlogn)
这个算法作业真是给初学者做的吗。。。P3 是省选模拟。。。这题更是想了两天半。
精简版证明:
我们想要用堆排序从大到小排序,维护一个大根堆。
不妨设 \(n=2^k-1\),那么堆是一棵完全二叉树。如果 \(n\) 不满足这个条件,可以先提取 \(n-2^k+1\) 个最大值,n 最多减半,在渐进意义下没有影响。
不妨假设元素为 1, 2,..., n,令 \(k=\lceil n/2\rceil\)。
把点分成两类,A 类是 \(\le k\),B 类是 \(>k\)。再把 A 类点分成叶子(0 类点)和非叶子(1 类点)。由于 A 类点构成若干个完全二叉树,所以叶子数量大于非叶子的。
那么在提取 \((n+1)/2\) 次最大值后,堆恰好成为高度 \(k-1\) 的完全二叉树。B 类点全部被取走,A 类点被取走一个。0 类点是被放到根的位置然后下沉的,所以所以 0 类点的最终下沉深度之和决定了时间复杂度。而下沉结束之后,是不会更深的,只会更浅。所以我们计算最终所有 0 类点的深度之和,必然小于等于下沉深度之和。
而 0 类点的深度之和,最小就是尽量放在深度浅的点,但是由于 0 类点数量大于等于 1 类点,就算放的再浅,也会把前 k-2 层都填满。而前 \(k-2\) 层的深度之和是
所以时间复杂度=下沉深度之和 $\ge $ 深度之和 \(=\Omega(n\log n)\)。
原版证明:
我们想要用堆排序从大到小排序,维护一个大根堆。
不妨设 \(n=2^k-1\),那么堆是一棵完全二叉树。如果 \(n\) 不满足这个条件,可以先提取 \(n-2^k+1\) 个最大值,n 最多减半,在渐进意义下没有影响。
我们先把点分成 AB 三类,A 类点表示大小排名 \(\ge (n+1)/2\) 的,B 类点排名 \(< (n+1)/2\) 的。
那么我们容易发现,B 类点的父亲一定是 B 类点。所以,如果某个点是 A 类点,那么它子树内所有点都是 A 类点。
那么,我们可以找到若干个“极高”的 A 类点,即他们的父亲都是 B 类点。这些点各自是一个完全二叉树的根。不妨假设这些完全二叉树的树高为 \(h_1,h_2,...,h_k\)。
那么显然有 \(\sum_i (2^{h_i}-1)=(n+1)/2\),即所有完全二叉树的大小加起来恰好是 A 类点的数量。
然后我们真正开始证明。我们把 A 类点分为两类,0 类代表是叶子,1 类代表不是叶子。那么 0 类点的数量为 \(\sum_i (2^{h_i-1})\ge \sum_i (2^{h_i}-1)/2\),超过了 A 类点的一半。
记 \(P=\sum_i (2^{h_i}-1)-\sum_i (2^{h_i-1}), Q=\sum_i (2^{h_i-1})\) 分别表示 1,0 类点的数量。接下来,我们进行 \((n+1)/2\) 次取根,然后把最后一个点的值放在根的位置,然后下沉的操作,即把所有 0 类点都取走并下沉,此时整棵二叉树的树高-1。
这个时候,整棵树都是 A 类点了,因为 B 类点已经被取完了,A 类点恰好取了一个,树上恰好有 \((n-1)/2\) 个点。我们开始计算 0 类点的总下沉复杂度。我们知道,一个点在下沉完毕之后,深度是不可能再变深的,只会因为其他点的下沉而上浮。即设权值 i 的下沉深度为 \(s_i\),在现在树中的深度为 \(dep_i\),则有 \(s_i\ge dep_i\)。我们证明 \(\sum dep_i=\Omega(n\log n)\)。我们计算所有 0 类点的深度之和。
我们知道,A 类点恰好取了一个,为了让结果更小,我们假设取了 0 类点。则 \(Q'=Q-1\)。由于 \(Q\ge P\),则 \(Q'\ge P-1\)。为了让 \(\sum dep_i\) 更小,我们把前深度浅的都填入 0 类点,最后的叶子数量 \(\ge (P+Q'+1)/2\ge P\),因此 1 类点最多能把叶子填完。
那么 \(n=2^k-1\),树有 k 层;取走 \((n+1)/2\) 个点后,就只有 k-1 层;而第 k-1 层被 1 类点填满了,所以 0 类点的深度和最小是前 k-2 层的深度和。
也即
这样也就证完了。

浙公网安备 33010602011771号