美文网首页排序算法
排序算法-3-堆排序

排序算法-3-堆排序

作者: 开了那么 | 来源:发表于2020-09-03 10:46 被阅读0次

    1、概念

    堆排序(英语:Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆的性质:即子节点的键值或索引总是小于(或者大于)它的父节点。(维基百科)

    2、运行过程

    1、思想概述

    若以升序排序说明,把数组转换成最大堆(Max-Heap Heap),这是一种满足最大堆性质(Max-Heap Property)的二叉树:对于除了根之外的每个节点i, A[parent(i)] ≥ A[i]。

    重复从最大堆取出数值最大的结点(把根结点和最后一个结点交换,把交换后的最后一个结点移出堆),并让残余的堆维持最大堆性质。

    2、堆的操作

    在堆的数据结构中,堆中的最大值总是位于根节点(在优先队列中使用堆的话堆中的最小值位于根节点)。堆中定义以下几种操作:

    • 最大堆调整(Max Heapify):将堆的末端子节点作调整,使得子节点永远小于父节点
    • 创建最大堆(Build Max Heap):将堆中的所有数据重新排序
    • 堆排序(HeapSort):移除位在第一个数据的根节点,并做最大堆调整的递归运算
    3、运行过程

    1、创建一个最大(小)堆H;

    2、把堆首和堆尾元素互换;

    3、把堆的大小减1,重新构造一个最大(小)堆;

    4、重复步骤2、3,直到堆的大小减少为1。

    (我们以最大堆为例)


    image

    3、代码

    java

    import java.util.Arrays;
    
    public class HeapSort {
        private int[] arr;
        public HeapSort(int[] arr) {
            this.arr = arr;
        }
    
        /**
         * 堆排序的主要入口方法,共两步。
         */
        public void sort() {
            /*
             *  第一步:将数组堆化
             *  beginIndex = 第一个非叶子节点。
             *  从第一个非叶子节点开始即可。无需从最后一个叶子节点开始。
             *  叶子节点可以看作已符合堆要求的节点,根节点就是它自己且自己以下值为最大。
             */
            int len = arr.length - 1;
            int beginIndex = (arr.length >> 1)- 1;
            for (int i = beginIndex; i >= 0; i--)
                maxHeapify(i, len);
            /*
             * 第二步:对堆化数据排序
             * 每次都是移出最顶层的根节点A[0],与最尾部节点位置调换,同时遍历长度 - 1。
             * 然后从新整理被换到根节点的末尾元素,使其符合堆的特性。
             * 直至未排序的堆长度为 0。
             */
            for (int i = len; i > 0; i--) {
                swap(0, i);
                maxHeapify(0, i - 1);
            }
        }
    
        private void swap(int i, int j) {
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
    
        /**
         * 调整索引为 index 处的数据,使其符合堆的特性。
         *
         * @param index 需要堆化处理的数据的索引
         * @param len 未排序的堆(数组)的长度
         */
        private void maxHeapify(int index, int len) {
            int li = (index << 1) + 1; // 左子节点索引
            int ri = li + 1;           // 右子节点索引
            int cMax = li;             // 子节点值最大索引,默认左子节点。
            if (li > len) return;      // 左子节点索引超出计算范围,直接返回。
            if (ri <= len && arr[ri] > arr[li]) // 先判断左右子节点,哪个较大。
                cMax = ri;
            if (arr[cMax] > arr[index]) {
                swap(cMax, index);      // 如果父节点被子节点调换,
                maxHeapify(cMax, len);  // 则需要继续判断换下后的父节点是否符合堆的特性。
            }
        }
    
        /**
         * 测试用例
         *
         * 输出:
         * [661,571,467,565,537,654,21,732,133,157,815,512,54,257,469,511,97,53,513,259,3,676,396,30,719,553,34,33,8,57, 69]
         */
        public static void main(String[] args) {
            int[] arr = new int[] {661,571,467,565,537,654,21,732,133,157,815,512,54,257,469,511,97,53,513,259,3,676,396,30,719,553,34,33,8,57, 69};
            new HeapSort(arr).sort();
            System.out.println(Arrays.toString(arr));
        }
    }
    

    4、性能

    堆排序的平均时间复杂度为O(nlog n),空间复杂度为O(1),是不稳定的排序算法。

    参考:
    十大经典排序算法(动图演示)

    相关文章

      网友评论

        本文标题:排序算法-3-堆排序

        本文链接:https://www.haomeiwen.com/subject/wnensktx.html