排序算法—堆排序

本文介绍了大根堆和小根堆的概念,其中大根堆的最大元素在根节点,小根堆的最小元素在根节点。堆排序通过构建大顶堆,交换堆顶与末尾元素并重新调整堆来实现排序。详细过程以[4,6,8,5,9]为例,逐步展示了堆排序的过程,并提供了大顶堆排序的Java代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

堆分为:

  • 大根堆
  • 小根堆

我先简单说一下大根堆的定义

可以将该一维数组视为一棵完全二叉树,大根堆的最大元素存放在根结点,且其任一非根结点的值小于等于其双亲结点值。

在这里插入图片描述

小根堆的话则和大根堆完全相反,即:小根堆的最小元素存放在根结点,且其任一非根结点的值大于等于其双亲结点值。
在这里插入图片描述

在这里插入图片描述

堆排序的基本思想是:

  • 1、将带排序的序列构造成一个大顶堆,根据大顶堆的性质,当前堆的根节点(堆顶)就是序列中最大的元素;
  • 2、将堆顶元素和最后一个元素交换,然后将剩下的节点重新构造成一个大顶堆;
  • 3、重复步骤2,如此反复,从第一次构建大顶堆开始,每一次构建,我们都能获得一个序列的最大值,然后把它放到大顶堆的尾部。

最后,就得到一个有序的序列了
下面我们通过一个简单的例子去了解一下:
假设我们的待排序数组为[4,6,8,5,9]
我们的起始节点即为最后一个非叶子结点,对应的下标为:length/2-1
5/2-1=1,所以我们从6开始
在这里插入图片描述

找到第二个非叶节点4,由于[4,9,8]中9元素最大,4和9交换,记住:我们每次都是和最大的子节点交换。
在这里插入图片描述

此时,交换导致了子根[4,5,6]结构混乱,继续调整,[4,5,6]中6最大,交换4和6。
在这里插入图片描述


我们非叶子结点交换完成之后,即i=0之后,输出堆顶元素(9),开始进行第二轮堆排序,将堆顶元素与末尾元素进行交换,使末尾元素最大。

然后继续调整堆,再将堆顶元素与末尾元素交换,得到第二大元素。如此反复进行交换、重建、交换。

  • (1)将堆顶元素9和末尾元素4进行交换,同时让length-1,即现在length变为4,我们待排序数组变为:[4,6,8,5],起始点:i=length/2-1=4/2-1=1,即从6开始进行调整,调整完成之后i-1=1-1=0,即下一个调整的点为nums[0]=4。
    在这里插入图片描述

  • (2)4和8进行交换
    在这里插入图片描述

由于i=0,所以输出堆顶元素:8,于是我们拿到了第二大元素,将最后一个元素5和8交换位置,同时length-1=4-1=3。

开始第三轮的堆排序,第三轮排序的数组为:[5,6,4]
起始下标为:3/2-1=0,即从nums[0]=5开始。

(1)5和6交换位置
在这里插入图片描述

由于i=0,我们拿到了第三大元素:6,输出6,交换6和4的位置,同时length-1=3-1=2;

开始第四轮堆排序,排序数组为:[4,5]
起始下标为:i=2/2-1=0,即从nums[0]=4,4和5交换位置
在这里插入图片描述

由于i=0,所以输出我们第四大元素:5,length-1=2-1=1,由于length<=1,所以输出最后一个元素:4,堆排序结束。

大顶堆代码实现:

   /**
     * 堆排序
     *
     * @param arr
     */
    public static void heapSort(int[] arr) {
        if (arr == null || arr.length == 0) {
            return;
        }
        int len = arr.length;
        // 构建大顶堆,这里其实就是把待排序序列,变成一个大顶堆结构的数组
        buildMaxHeap(arr, len);

        // 交换堆顶和当前末尾的节点,重置大顶堆
        for (int i = len - 1; i > 0; i--) {
            swap(arr, 0, i);
            len--;
            heapify(arr, 0, len);
        }
    }

    private static void buildMaxHeap(int[] arr, int len) {
        // 从最后一个非叶节点开始向前遍历,调整节点性质,使之成为大顶堆
        for (int i = (int) Math.floor(len / 2) - 1; i >= 0; i--) {
            heapify(arr, i, len);
        }
    }

    private static void heapify(int[] arr, int i, int len) {
        // 先根据堆性质,找出它左右节点的索引
        int left = 2 * i + 1;
        int right = 2 * i + 2;
        // 默认当前节点(父节点)是最大值。
        int largestIndex = i;
        if (left < len && arr[left] > arr[largestIndex]) {
            // 如果有左节点,并且左节点的值更大,更新最大值的索引
            largestIndex = left;
        }
        if (right < len && arr[right] > arr[largestIndex]) {
            // 如果有右节点,并且右节点的值更大,更新最大值的索引
            largestIndex = right;
        }

        if (largestIndex != i) {
            // 如果最大值不是当前非叶子节点的值,那么就把当前节点和最大值的子节点值互换
            swap(arr, i, largestIndex);
            // 因为互换之后,子节点的值变了,如果该子节点也有自己的子节点,仍需要再次调整。
            heapify(arr, largestIndex, len);
        }
    }

    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZNineSun

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值