• 常见的排序算法(七):堆排序


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

      堆是通过一维数组来实现的。在数组起始位置为0(根节点)的情形中:

    • 父节点i的左子节点在位置(2i+1)

    • 父节点i的右子节点在位置(2i+2)

    • 子节点i的父节点在位置floor((i-1)/2)

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

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

      通俗来说就是把数组维持成一个最大堆,最大堆根节点最大的数值,然后与数组最后一个元素交换,并把最后那个位置移除堆,剩下的堆因为交换过去的元素破坏最大堆的性质,所以要维持最大堆的性质,再继续把最大的根节点与堆数组的最后一个交换,重复值堆中只有两个元素,最后两个元素比较后叫完成了排序。

    堆排序算法如下:

      维持最大堆的函数

     1     /**
     2      * 调整索引为 index 处的数据,使其符合堆的特性。
     3      *
     4      * @param index 需要堆化处理的数据的索引
     5      * @param len 未排序的堆(数组)的长度
     6      */
     7     private void maxHeapify(int index, int len) {
     8         int li = (index << 1) + 1;  // 左子节点索引
     9         int ri = li + 1;            // 右子节点索引
    10         int cMax = li;              // 子节点值最大索引,默认左子节点。
    11         if(li > len)                // 左子节点索引超出计算范围,直接返回。
    12             return;
    13         if(ri <= len && arr[ri] > arr[li]) // 先判断左右子节点,哪个较大。
    14             cMax = ri;
    15         if(arr[cMax] > arr[index]) {
    16             swap(cMax, index);      // 如果父节点被子节点调换,
    17             maxHeapify(cMax, len);  // 则需要继续判断换下后的父节点是否符合堆的特性。
    18         }
    19     }

    堆调整的时间复杂度

      从堆调整的代码可以看到是当前节点其值较大的子节点(子节点比较一次)比较,交换一次。父节点与哪一个子节点进行交换,就对该子节点递归进行此操作,设对调整的时间复杂度为T(k)(k为该层节点到叶节点的距离),那么有
      T(k)=T(k-1)+3, k∈[2,h]
      T(1)=3
      迭代法计算结果为:
        T(h)=3h=3 floor(log n)
      所以堆调整的时间复杂度是O(log n) 。

    建堆的时间复杂度:

      n个节点的堆,树高度是h=floor(log n)。对深度为于 h-1 层的节点,比较2次,交换1次,这一层最多有2^(h-1)个节点,总共操作次数最多为3*2^(h-1));对深度为h-2层的节点,总共有2^(h-2)个,每个节点最多比较4次,交换2次,所以操作次数最多为3*2^(h-2))……
      以此类推,从最后一个父节点到根结点进行堆调整的总共操作次数为O(n),建堆时间复杂度为O(n)。

      空间复杂度O(1)。

      堆排序入口:

     1     /**
     2      * 堆排序的主要入口方法,共两步。
     3      */
     4     public void sort() {
     5         /*
     6          *  第一步:将数组堆化
     7          *  beginIndex = 第一个非叶子节点。
     8          *  从第一个非叶子节点开始即可。无需从最后一个叶子节点开始。
     9          *  叶子节点可以看作已符合堆要求的节点,根节点就是它自己且自己以下值为最大。
    10          */
    11         int len = arr.length - 1;
    12         int beginIndex = (arr.length >> 1) - 1;
    13         for(int i = beginIndex; i>=0; i--) {
    14             maxHeapify(i, len);
    15         }
    16         /*
    17          * 第二步:对堆化数据排序
    18          * 每次都是移出最顶层的根节点A[0],与最尾部节点位置调换,同时遍历长度 - 1。
    19          * 然后从新整理被换到根节点的末尾元素(比较小),使其符合堆的特性。
    20          * 直至未排序的堆长度为 0。
    21          */
    22         for(int i = len; i > 0; i--) {
    23             swap(0, i);
    24             maxHeapify(0, i-1);
    25         }
    26     }

      测试代码:

    1     public static void main(String[] args) {
    2         int[] arr = new int[] {3, 5, 3, 0, 8, 6, 1, 5, 8, 6, 2, 4, 9, 4, 7, 0, 1, 8, 9, 7, 3, 1, 2, 5, 9, 7, 4, 0, 2, 6};
    3         new HeapSort(arr).sort();
    4         System.out.println(Arrays.toString(arr));
    5     }
  • 相关阅读:
    C语言I博客作业08
    作业7
    作业6
    作业5
    作业--4
    java基础学习--I/O流
    刷题记录--[CISCN2019 华北赛区 Day2 Web1]Hack World
    ADB测试Android真机
    sqli-labs通关笔记
    Tensorflow入门
  • 原文地址:https://www.cnblogs.com/magic-sea/p/11374242.html
Copyright © 2020-2023  润新知