本文主要记录对堆排序中建立初始大根堆部分的时间复杂度的分析。结论为 O(n)。
代码
代码主要分为两部分,堆排序的主方法以及调整堆的方法,此处的堆为大根堆,对应升序排序。
堆排序主方法
1 | public static void HeapSort(int[] nums) |
调整堆
1 | private static void HeapAdjust(int[] nums, int root, int end) |
分析及证明
直接看代码的话可能会得出建立初始大根堆的时间复杂度为 O(nlogn) 的结论。
主方法中是一个 n/2 次的循环,调整堆的方法是一个 logn 次的循环,相乘确实是 O(nlogn) 没错。
事实上,每个节点并没有进行那么多次的调整。
接下来进行证明:(PS:以下的证明中,树的高度基数为 0,即叶子节点的高度为 0)
前提:建立初始大根堆的顺序是自下而上的。
首先,有一棵完全二叉树,具有 n 个元素,那么树的高度 h = logn。
调整堆是从这棵树的最后一个非叶子节点开始的,而该节点所在的层的高度为 1,因此该层节点只需要向下进行 1 次运算就能调整完成。
在调整上层元素时,不需要和下层的所有元素进行比较,只需要和其中之一的分叉进行比较即可,比较次数即为节点所在层的高度,因此,第 i 层的元素的计算量为 2^(h - i) * i。
由以上的通项公式可以得知,建立初始大根堆的精确时间复杂度为:
该求和公式为等比数列与等差数列的乘积,因此可以使用错位相减法求解:
用 2S 减去 S 得:
由等比数列求和公式可得:
将 h = logn 带入 S 中得:
因此,可以得出结论:建立初始大根堆的时间复杂度为 O(n)。