堆排序(Heap Sort)JAVA实现

本文深入解析堆排序算法,包括堆的定义、大顶堆与小顶堆的区别,以及堆排序的基本思想和过程。通过代码实现展示了如何构建堆并进行排序,适用于计算机科学与数据结构的学习者。

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

堆排序(Heap Sort)

堆的定义如下:n个元素的序列{k1,k2,···,kn},当且仅当满足下关系时,称之为堆。
小顶堆:ki <= k(2i) 且 ki <= k(2i+1)

大顶堆: ki >= k(2i) 且 ki >= k(2i+1)
堆构造

把此序列对应的二维数组看成一个完全二叉树。那么堆的含义就是:完全二叉树中任何一个非叶子节点的值均不大于(或不小于)其左,右孩子节点的值。由上述性质可知大顶堆的堆顶的关键字肯定是所有关键字中最大的,小顶堆的堆顶的关键字是所有关键字中最小的。因此我们可使用大顶堆进行升序排序, 使用小顶堆进行降序排序

基本思想
此处以大顶堆为例,堆排序的过程就是将待排序的序列构造成一个堆,选出堆中最大的移走,再把剩余的元素调整成堆,找出最大的再移走,重复直至有序。

由于堆排序中初始化堆的过程比较次数较多, 因此它不太适用于小序列。同时由于多次任意下标相互交换位置, 相同元素之间原本相对的顺序被破坏了, 因此, 它是不稳定的排序

代码描述
从算法描述来看,堆排序需要两个过程,即两个函数,1. 建立堆,需要建堆函数;2.堆顶与堆的最后一个元素交换位置,复调用建堆函数以选择出剩余未排元素中最大的数来实现排序的函数。

  1. 先将初始序列K[1…n]建成一个大顶堆, 那么此时第一个元素K1最大, 此堆为初始的无序区;
  2. 再将关键字最大的记录K1 (即堆顶, 第一个元素)和无序区的最后一个记录 Kn 交换, 由此得到新的无序区K[1…n−1]和有序区K[n], 且满足K[1…n−1].keys ⩽ K[n].key
  3. 交换K1 和 Kn 后, 堆顶可能违反堆性质, 因此需将K[1…n−1]调整为堆. 然后重复步骤②, 直到无序区只有一个元素时停止.

总结起来就是定义了以下几种操作:
最大堆调整(Max_Heapify):将堆的末端子节点作调整,使得子节点永远小于父节点,即创建最大堆
堆排序(HeapSort):移除位在第一个数据的根节点,并做最大堆调整的递归运算

对于堆节点的访问:
父节点i的左子节点在位置:(2 * i+1);
父节点i的右子节点在位置:(2 * i+2);
子节点i的父节点在位置:floor((i-1)/2); floor()向下取整

①. 建立堆的过程, 从length/2 一直处理到0, 时间复杂度为O(n);
②. 调整堆的过程是沿着堆的父子节点进行调整, 执行次数为堆的深度, 时间复杂度为O(log2n);
③. 堆排序的过程由n次第2步完成, 时间复杂度为O(n log2n).

在这里插入图片描述
代码实现

public class Sort{

  public static void heapSort(int[] arr){
    for(int i = arr.length; i > 0; i--){
        max_heapify(arr, i);   //得到大顶堆
        int temp = arr[0];    //堆顶元素(第一个元素)与Kn交换
        arr[0] = arr[i-1];
        arr[i-1] = temp;
    }
 }
  private static void max_heapify(int[] arr, int limit){
    if(arr.length <= 0 || arr.length < limit) return;     //limit表示剩余的无序数组的长度
    for(int parentIdx = limit/2; parentIdx >= 0; parentIdx--){
        if(parentIdx * 2 >= limit){
            continue;
        }
        int left = parentIdx * 2;       //左子节点位置
        int right = (left+1) >= limit ? left : (left + 1);   //右子节点位置,如果没有右节点,默认为左节点位置
        int maxChildId = arr[left] >= arr[right] ? left : right;
        if(arr[maxChildId] > arr[parentIdx]){   //交换父节点与左右子节点中的最大值
            int temp = arr[parentIdx];
            arr[parentIdx] = arr[maxChildId];
            arr[maxChildId] = temp;
        }
    }
 }

 public static void main(String[] args) {
        int[] arr = {2,8,10,6,0,12,4};
        heapSort(arr);
        for (int i : arr){
            System.out.print(i + " ");
        }
  }
}

小顶堆排序 降序

public class Sort{

    public static void heapSort(int[] arr){
        for(int i = arr.length; i > 0; i--){
            min_heapify(arr, i);   //得到小顶堆
            int temp = arr[0];    //堆顶元素(第一个元素)与Kn交换
            arr[0] = arr[i-1];
            arr[i-1] = temp;
        }
    }
    private static void min_heapify(int[] arr, int limit){
        if(arr.length <= 0 || arr.length < limit) return;     //limit表示剩余的无序数组的长度
        for(int parentIdx = limit/2; parentIdx >= 0; parentIdx--){
            if(parentIdx * 2 >= limit){
                continue;
            }
            int left = parentIdx * 2;       //左子节点位置
            int right = (left+1) >= limit ? left : (left + 1);   //右子节点位置,如果没有右节点,默认为左节点位置
            int minChildId = arr[left] <= arr[right] ? left : right;
            if(arr[minChildId] < arr[parentIdx]){   //交换父节点与左右子节点中的最小值
                int temp = arr[parentIdx];
                arr[parentIdx] = arr[minChildId];
                arr[minChildId] = temp;
            }
        }
    }

    public static void main(String[] args) {
        int[] arr = {2,8,10,6,0,12,4};
        heapSort(arr);
        for (int i : arr){
            System.out.print(i + " ");
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值