对于堆排序的从下到上的建堆过程为什么时间复杂度是O(n)而不是O(nlogn)

堆排序的第一步:建堆过程时间度是O(n)。

以升序为例,我们知道对于堆排序的第一步,是要先建立一个大顶堆。代码如下:

void adjustHeap(vector<int>& arr, int parent, int length){
    int temp = arr[parent];//寻找temp最终应该放的位置
    while(parent*2+1<length){
        int left = parent*2+1;
        if((left+1)<length&&arr[left]<arr[left+1])
            left = left+1;
        if(arr[left]>temp){
            arr[parent] = arr[left];
            //更新parent
            parent = left;
        }else break;
    }
    arr[parent] = temp;
}
for(int i=arr.size()/2-1;i>=0;i--){
        adjustHeap(arr, i, arr.size());
}

给定一个固定的序列,将其从下到上建成一个大顶堆的过程如下:
假定堆以数组实现,建堆的过程,就是不断将一个完全二叉树进行调整使得该完全二叉树满足: 每一棵子树的根节点总是比其左子树和右子树的值大。这个调整过程主要是: 从第一个非叶子结点开始不断地从下往上调整(如果假定下标从0开始,则第一个非叶子结点的下标就是arr.length/2-1),故外循环是O(n)。针对每次调整,调整函数ajustHeap之所以里面加一个while, 主要是因为,针对当前的根节点parent,当和其左孩子和右孩子交换之后,有可能破坏了其左子树或者右孩子的堆结构,因此还要往下继续调整,也就是找到最初的arr[parent]最终应该放置的位置。由于该while循环中,针对parent结点,它在往下调整找合适位置的时候,总是找某一半的子树(每一次要么往左子树走,要么是往右子树走),所以针对某一次调整,其调整的时间复杂度是树高: O(logn)。所以直观上从整体上来看,建堆的时间复杂度是外层的for循环乘内层的O(logn)的复杂度是:O(nlogn)。其实,这是不正确的。构建二叉堆是自下而上的构建,每一层的最大深度总是小于等于树的深度的,因此,该问题是叠加问题,不是递归问题。那么换个方式,假如我们自上而下建立二叉堆,那么插入每个节点都和树的深度有关,并且都是不断的把树折半来实现插入,因此是典型的递归,而非叠加。时间复杂度是O(nlogn)。
下面给出证明过程:
针对一棵完全二叉树,设树高为h, 则当前层元素最多 2 x − 1 2^{x-1} 2x1个,x为层数,由于从第一个非叶子结点进行调整,第一层的非叶子结点的层数也就是倒数第二层,因此开始调整的第一层的个数为 2 h − 2 2^{h-2} 2h2,对于该层只需要做一次线性比较就可以调整成功。依次往上考虑,对于倒数第二层,其结点的个数为 2 h − 3 2^{h-3} 2h3,其需要再往下调整的层数最多为2。故对于第倒数x层,其元素的个数为 2 h − x 2^{h-x} 2hx, 其往下的比较次数和处理时间复杂度也就是往下的高度减1,即 x − 1 x-1 x1。对于第1层,即倒数第h层,有1个元素,往下的调整的层数为 h − 1 h-1 h1。设总的时间复杂度为S:
S = 1 ∗ ( h − 1 ) + 2 ∗ ( h − 2 ) + 4 ∗ ( h − 3 ) + . . . + 2 h − 4 ∗ 3 + 2 h − 3 ∗ 2 + 2 h − 2 ∗ 1 S = 1* (h-1) + 2 * (h-2) + 4*(h-3) +...+ 2^{h-4} * 3 + 2^{h-3} *2+ 2^{h-2} *1 S=1(h1)+2(h2)+4(h3)+...+2h43+2h32+2h21 (1)
观察上式,属于等差数列和等比数列的乘积的累加和,可以使用裂项错位相减。
(1)乘以2得:
2 S = 2 ∗ ( h − 1 ) + 4 ∗ ( h − 2 ) . . . + 2 h − 3 ∗ 3 + 2 h − 2 ∗ 2 + 2 h − 1 ∗ 1 2S = 2*(h-1) +4*(h-2)...+ 2^{h-3} * 3 + 2^{h-2} *2+ 2^{h-1} *1 2S=2(h1)+4(h2)...+2h33+2h22+2h11 (2)
(2)-(1) = S = − ( h − 1 ) + 2 + 4 + . . . + 2 h − 3 + 2 h − 2 + 2 h − 1 -(h-1)+2 + 4 +...+2^{h-3}+2^{h-2} + 2^{h-1} (h1)+2+4+...+2h3+2h2+2h1
后面是一个等比数列,使用等比数列的求和公式 a 1 − a n ∗ q 1 − q \frac{a_1-a_n * q}{1-q} 1qa1anq S = 2 h − 2 − h + 1 = 2 h − h − 1 S = 2^h-2-h+1 = 2^h-h-1 S=2h2h+1=2hh1。将h = logn带入得S = n - logn -1,故最终的时间复杂度为O(n-logn-1) = O(n)。

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值