【基础算法】堆排序

具体的演示过程可以参考:https://www.cnblogs.com/chengxiao/p/6129630.html

以大堆顶为例,有:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]

// 最大堆排序,可以得到递增序列
public void heapSort(int[] a) {
    if (a == null || a.length == 0) {
        return;
    }
    // 从后往前遍历所有元素,构建出最大堆
    // 从前往后遍历是为了在一个已经整理好的堆上插入新的元素
    // 这里使用原地算法,当插入 a[i] 时, a[i+1,maxLen] 已经是一个整理好的堆了
    // 即 a[i+1,maxLen] 不能再被乱动
    // 所以遍历元素时从前往后
    for (int i = a.length - 1; i >= 0; i--) {
        buildBigHeap(i, a, a.length);
    }
    // 有了最大堆之后,每次都把堆顶的与当前末尾元素交换,则可以得到升序排序的最终序列
    for (int i = a.length - 1; i > 0; i--) {
        Common.sweap(a, 0, i);// sweap() 方法用于交换数组中的两个元素
        // 排除掉当前末尾的元素,重新构建最大堆
        buildBigHeap(0, a, i - 1);
    }
}

// 构建最大堆
private void buildBigHeap(int curIndex, int[] a, int curLen) {
    int childLeft = curIndex * 2 + 1;
    int chileRight = childLeft + 1;
    int tmpMaxIndex = childLeft;
    if (childLeft >= curLen) return;
    // 找到两个子节点中最大的那一个
    if (chileRight < curLen && a[tmpMaxIndex] < a[chileRight]) {
        tmpMaxIndex = chileRight;
    }
    if (a[tmpMaxIndex] > a[curIndex]) {
        Common.sweap(a, tmpMaxIndex, curIndex);
        // 把当前父节点设置成包含其子节点中最大的那一个后,
        // 被设置的那个子节点也需要跟着调整
        // 因为该子节点也会是下一级的一个父节点
        // 即此时包含该子节点的子树会受到影响
        buildBigHeap(tmpMaxIndex, a, curLen);
    }
}

堆排序也是一种不稳定的排序。

因为使用的原地算法,所以空间复杂度为 O(1)

对于时间复杂度的话,先说结论,它的最坏,最好,平均时间复杂度均为 O(n log n)

对于时间复杂度的,主要分为两部分,一个是建堆的过程,另一个是每次都把堆顶的与当前元素末尾交换,则可以得到升序排序的最终序列的过程

(1)堆排序中建堆过程时间复杂度 O(n) 的由来,参考自:https://www.zhihu.com/question/20729324
在这里插入图片描述
(2)在交换并重建堆的过程中,重建堆一共需要 (n-1) 次循环(n 为需要排序的元素个数):

第一次循环中 ,将堆顶的元素与最后一个交换,之后重建堆的时候就只有 (n-1) 个元素了,此时有比较次数 log (n-1);

第二次循环中 ,将堆顶的元素与倒数第二个交换,之后重建堆的时候就只有 (n-2) 个元素了,此时有比较次数 log (n-2);

因此,总共有 log (n-1) + log (n-2) + ... + log 2 = log ((n-1)*(n-2)*...*2) = log ((n-1)!) ≈ n log n

因此总的为 O(n + n log n),所以堆排序时间复杂度一般认为就是 O(n log n) 级。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值