堆排序

209 阅读3分钟

堆排序是著名的原地且不稳定的排序算法之一。顾名思义,堆排序是利用数据结构-堆(Heap)。
堆有两个特点:

  1. 堆是一个完全二叉树(完全二叉树要求除了最后一层,其他层的节点都是满的,最后一层的节点都靠左排列。)
  2. 堆中每个节点都必须大于等于(或小于等于)其子树中每个节点的值。

堆的时间复杂度:
最好, 最坏,平均都是O(nlogn)。

堆分为两类:

  1. 大顶堆, 顶点为最大值。
  2. 小顶堆, 顶点为最小值。
public class HeapSort {
    // 堆排序是原地且不稳定的算法,不稳定的原因是需要把叶子节点和堆顶节点进行交换
    // 最好,最坏,平均时间复杂度都是O(nlogn)
    // 堆的定义:堆是一个完全二叉树(完全二叉树要求除了最后一层,其他层的节点都是满的,最后一层的节点都靠左排列。),堆中每个节点都必须大于等于(或小于等于)其子树中每个节点的值。
    // 堆排序有两种类型:大顶堆,小顶堆
    // 大顶堆:节点的大于子节点, 小顶堆:节点小于子节点
    // 利用: 求Top K问题, 求中位数问题
    // 堆的构建:从数组下标1开始使用,即堆顶元素的下标为0, 一个节点的下标为i,其子节点为2*i+1和2*i+2,其父节点为(i-1)/2

    /**
     * 利用小顶堆来从小到大的排序
     *
     * @param data 数组
     * @param n    数组的长度
     * @return
     */
    public static void minHeapSort(int[] data, int n) {
        if (n <= 1) return;

        // 建堆
        buildMinHeap(data);

        // 排序
        int k = data.length - 1;
        while (k > 0) {
            // 将堆顶元素(最大)与最后一个元素交换位置
            swap(data, 0, k);
            // 将剩下元素重新堆化,堆顶元素变成最大元素
            heapifyInMinHeap(data, --k, 0);
        }

    }

    /**
     * 利用大顶堆来从大到小的排序
     *
     * @param data 数组
     * @param n    数组的长度
     * @return
     */
    public static void maxHeapSort(int[] data, int n){
        if(n<=1) return;

        // 建堆
        buildMaxHeap(data);

        // 排序
        int k = data.length - 1;
        while (k > 0) {
            // 将堆顶元素(最小)与最后一个元素交换位置
            swap(data, 0, k);
            // 将剩下元素重新堆化,堆顶元素变成最大元素
            heapifyInMaxHeap(data, --k, 0);
        }
    }

    /**
     * 建小顶堆
     *
     * @param data 数组
     */
    private static void buildMinHeap(int[] data) {
        // (arr.length) / 2 为最后一个叶子节点的父节点
        // 也就是最后一个非叶子节点,依次堆化直到根节点
        // 这里i>=1的原因是,堆顶是从下标1开始储存
        for (int i = (data.length - 1) / 2; i >= 0; i--) {
            heapifyInMinHeap(data, data.length - 1, i);
        }
    }

    /**
     * 建大顶堆
     *
     * @param data 数组
     */
    private static void buildMaxHeap(int[] data) {
        for (int i = (data.length - 1) / 2; i >= 0; i--) {
            heapifyInMaxHeap(data, data.length - 1, i);
        }
    }

    /**
     * 小顶堆的堆化,为了删除或者添加数据时使数据结构满足堆的要求
     *
     * @param data 数组
     * @param n    数组的长度
     * @param i    要堆化的下标
     * @return
     */
    private static void heapifyInMinHeap(int[] data, int n, int i) {
        while (true) {
            int maxPos = i;
            if (2 * i + 1 < n && data[i] < data[2 * i + 1]) maxPos = 2 * i + 1;
            if (2 * i + 2 < n && data[maxPos] < data[2 * i + 2]) maxPos = 2 * i + 2;
            if (maxPos == i) break;
            swap(data, i, maxPos);
            i = maxPos;
        }
    }

    /**
     * 大顶堆的堆化,为了删除或者添加数据时使数据结构满足堆的要求
     *
     * @param data 数组
     * @param n    数组的长度
     * @param i    要堆化的下标
     * @return
     */
    private static void heapifyInMaxHeap(int[] data, int n, int i) {
        while (true) {
            int minPos = i;
            if (2 * i + 1 < n && data[i] > data[2 * i + 1]) minPos = 2 * i + 1;
            if (2 * i + 2 < n && data[minPos] > data[2 * i + 2]) minPos = 2 * i + 2;
            if (minPos == i) break;
            swap(data, i, minPos);
            i = minPos;
        }
    }

    /**
     * 数组中数据的交换
     *
     * @param data  数组
     * @param left  要交换的下标
     * @param right 要交换的下标
     */
    private static void swap(int[] data, int left, int right) {
        int tmp = data[left];
        data[left] = data[right];
        data[right] = tmp;
    }
}