数据结构与算法系列——排序(7)_堆排序

1. 工作原理(定义)

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

  1. 大顶堆:每个节点的值都大于或等于其子节点的值,在堆排序算法中用于升序排列;arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]  
  2. 小顶堆:每个节点的值都小于或等于其子节点的值,在堆排序算法中用于降序排列;arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]  

  同时,我们对堆中的结点按层进行编号,将这种逻辑结构映射到数组中就是下面这个样子

  

  该数组从逻辑上讲就是一个堆结构,我们用简单的公式来描述一下堆的定义就是:

2. 算法步骤

  1. 创建一个最大堆 H[0……n-1];

  2. 把堆首(最大值)和堆尾互换;

  3. 把堆的尺寸缩小 1,并调用 shift_down(0),目的是把新的数组顶端数据调整到相应位置;

  4. 重复步骤 2,直到堆的尺寸为 1。

   创建最大堆(Build-Max-Heap)的作用是将一个数组改造成一个最大堆,,接受数组和堆大小两个参数,Build-Max-Heap 将自下而上的调用 Max-Heapify 来改造数组,建立最大堆【注意从非叶子节点开始】。因为 Max-Heapify 能够保证下标 i 的结点之后结点都满足最大堆的性质,所以自下而上的调用 Max-Heapify 能够在改造过程中保持这一性质。

  

  堆排序(Heap-Sort)是堆排序的接口算法,Heap-Sort先调用Build-Max-Heap将数组改造为最大堆,然后将堆顶和堆底元素交换,之后将底部上升,最后重新调用Max-Heapify保持最大堆性质【因为 Max-Heapify 能够保证下标 i 的结点之后结点都满足最大堆的性质,所以自下而上的调用 Max-Heapify 能够在改造过程中保持这一性质。】。由于堆顶元素必然是堆中最大的元素,所以一次操作之后,堆中存在的最大元素被分离出堆,重复n-1次之后,数组排列完毕。整个流程如下:

  

3. 动画演示

4. 性能分析

1. 时间复杂度

  堆排序的时间等于建堆和进行堆调整的时间。

  构建最大堆大概需进行n/2 * 2 = n次比较和n/2次交换,故时间复杂度为O(n)。

  堆调整的时间为(n - 1)*O(log2(n))。

  堆排序的时间复杂度为O(n*logn)。

  在最优、最坏和平均情况下,其时间复杂度为 O(n*logn)。

2. 空间复杂度

  堆排序为就地排序,因此空间复杂度为O(1)。

3. 算法稳定性 

  堆排序是不稳定的算法,在构建堆过程中元素的顺序可能会发生变化。

4. 初始顺序状态

  1. 比较次数:
  2. 移动次数:
  3. 复杂度:    
  4. 排序趟数:

5. 归位

  能归位,每一趟排序有一个元素归位。

6. 优点

6. 具体代码

import java.util.Arrays;

public class HeapSort {
    public static void main(String[] args) {
        int [] array = new int[]{2,3,5,6,7,8,23,1,9};
        array = heapSort(array);
        for (int i = 0; i < array.length; i++) {
            System.out.print(array[i]+" ");
        }
    }
    // 堆排序(HeapSort):移除位在第一个数据的根节点,并做最大堆调整的递归运算
    public static int[] heapSort(int[] sourceArray) {
        // 对 arr 进行拷贝,不改变参数内容
        int[] arr = Arrays.copyOf(sourceArray, sourceArray.length);
        int len = arr.length;
        // 建立最大堆
        buildMaxHeap(arr, len);
        // 首尾元素交换,长度减一,尾部元素最大
        for (int i = len - 1; i > 0; i--) {
            swap(arr, 0, i);
            len--;
            heapify(arr, 0, len);
        }
        return arr;
    }

    /**
     *  创建最大堆(Build-Max-Heap)的作用是将一个数组改造成一个最大堆,接受数组和堆大小两个参数,
     *  Build-Max-Heap 将自下而上的调用 Max-Heapify 来改造数组,建立最大堆【注意从非叶子节点开始】。
     *  因为 Max-Heapify 能够保证下标 i 的结点之后结点都满足最大堆的性质,所以自下而上的调用 Max-Heapify 能够在改造过程中保持这一性质。
     */
    private static void buildMaxHeap(int[] arr, int len) {
        // 非叶子节点开始
        for (int i = (int) Math.floor(len / 2); i >= 0; i--) {
            heapify(arr, i, len);
        }
    }
     
    // 最大堆调整(Max Heapify):将堆的末端子节点作调整,使得子节点永远小于父节点
    private static void heapify(int[] arr, int i, int len) {
        int left = 2 * i + 1;
        int right = 2 * i + 2;
        int largest = i;

        if (left < len && arr[left] > arr[largest]) {
            largest = left;
        }
        if (right < len && arr[right] > arr[largest]) {
            largest = right;
        }
        if (largest != i) {//保证下标 i 的结点之后结点都满足最大堆的性质
            swap(arr, i, largest);
            heapify(arr, largest, len);
        }
    }

    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}

7. 参考网址

  1. 数据结构基础学习笔记目录
  2. 常见排序算法 - 堆排序 (Heap Sort)
  3. 图解排序算法(三)之堆排序
  4. 排序算法系列之堆排序
  5. https://visualgo.net/en/sorting
  6. https://www.runoob.com/w3cnote/heap-sort.html
  7. https://github.com/hustcc/JS-Sorting-Algorithm
原文地址:https://www.cnblogs.com/haimishasha/p/10842905.html