堆排序新解

简介

  • 堆排序是一种不稳定的排序算法。
  • 堆排序的时间复杂度为O(NlogN)。
  • 堆排序有两种实现方式。基于递归函数的实现,其额外空间复杂度为O(logN);非递归实现的额外空间复杂度为O(1)。

这里讨论的是非递归的实现方式。堆分为大根堆和小根堆,是完全二叉树
图一:完全二叉树示例

对于完全二叉树中的任意一个节点,若它存在左孩子和右孩子(右孩子如果缺失可以脑补),你都会发现如图一三角形区域所示的微金字塔结构。

这里说明一点,全篇的关注焦点都应该放在这个微金字塔结构中。复杂的完全二叉树可以将它抽象成多个微金字塔结构的堆叠。堆排序的最小操作单元可以认为是微金字塔结构中的节点。

大根堆和小根堆

大根堆是什么?对于堆中任意一个微金字塔结构内的节点,如果最大值的节点在金字塔顶端,那么它就是大根堆。同理,如果在任意一个微金字塔结构中,最小值的节点在金字塔顶端,那么它就是小根堆。

用公式描述如下:

大根堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
小根堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]
其中,i为节点所在的位置,2i+1为左孩子的位置,2i+2为右孩子的位置。

排序步骤

这里以大根堆为例。大根堆排序的步骤如下:

  1. 将长度为N的待排序数组构造成一个大根堆。
  2. 将根节点与尾节点交换。
  3. 将去除尾节点后剩余的N -1个节点重新构造成一个大根堆。
  4. 重复步骤2,步骤3直至成为一个有序序列。
构造大根堆

排序步骤中的关键点是如何构造一个大根堆。我们从微观层面着眼,构造大根堆就是调节堆中的每一个微金字塔结构,使任意一个微金字塔结构中节点的最大值都在其顶部。

如何调整微金字塔中的节点?找出最大值节点,如果不在微金字塔的顶端,交换位置使它处于顶部。这里有个注意点,如果该微金字塔结构重新调整了,那么它上层的微金字塔结构可能不再符合大根堆的要求,可能需要从该位置起向上层重新调整一遍。

代码实现

  • 主函数
    public static void heapSort(char[] chas) {
        //构造一个大根堆
        for (int i = 0; i < chas.length; i++) {
            maxHeapFirstBuild(chas, i);
        }
        for (int i = chas.length - 1; i > 0; i--) {
            // 首尾交换
            swap(chas, 0, i);
            //构造一个新的大根堆
            maxHeapify(chas, 0, i);
        }
    }
  • 待排序数组生成大根堆
  private static void maxHeapFirstBuild(char[] chas, int i) {
        int parent = 0;
        while (i != 0) {
            parent = (i - 1) / 2;
            if (chas[parent] < chas[i]) {
                swap(chas, parent, i);
                i = parent;
            } else {
                break;
            }
        }
    }
  • 核心代码段
 private static void maxHeapify(char[] chas, int i, int size) {
        int left = i * 2 + 1;
        int right = i * 2 + 2;
        int largest = i;
        //判断当前节点有没有左孩子
        while (left < size) {
            if (chas[left] > chas[i]) {
                largest = left;
            }
            if (right < size && chas[right] > chas[largest]) {
                largest = right;
            }
            if (largest != i) {
                swap(chas, largest, i);
            } else {
                break;
            }
            i = largest;
            left = i * 2 + 1;
            right = i * 2 + 2;
        }
    }

while循环内部是微金字塔结构的调整逻辑,如果该微金字塔结构调整过,则回溯。

  • 首尾交换
  public static void swap(char[] chas, int index1, int index2) {
        char tmp = chas[index1];
        chas[index1] = chas[index2];
        chas[index2] = tmp;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值