一起排序01-堆排序

388 阅读2分钟

2021/02/27

堆排序的基本思想

堆排序(HeapSort)是利用 二叉堆 进行排序的排序算法,升序排序使用最大堆,降序排序使用最小堆

本文以升序排序为例。

堆排序的步骤如下:

  • 用待排序列构建一个大顶堆,n=heap.size()
  • 循环如下操作,直到 n==0
    • 将堆顶元素与第 n 个元素交换,n=n−1
    • 对前 n 个元素重新调整为大顶堆

也就是说,因为大顶堆的堆顶始终是最大元素,每次将最大元素经过交换后放在堆的最后,经过 n−1 次调整后,就可以得到一个升序序列。

例如对一个无序序列:11, 22, 11, 3, 12, 15, 2, 21, 47 进行升序排序,具体过程如下:

首先构建一个大顶堆,

然后堆顶元素47和堆最后元素3交换,并将 除47 的所有元素重新调整为大顶堆,

交换 223,并将 除22,47 的所有元素重新调整为大顶堆,

交换 212,并将 除21,22,47 的所有元素重新调整为大顶堆,

交换 152,并将 除15,21,22,47 的所有元素重新调整为大顶堆,

交换 123 ,并将 除12,15,21,22,47 的所有元素重新调整为大顶堆,

继续操作,直到只剩一个元素时就可以停止了,

最终的堆就是排序的结果 2, 3, 11 ,11 ,12 ,15 ,21 ,22 ,47

以上就是堆排序的过程演示,下面用代码实现。

代码实现

堆排序的交换操作,相当于堆的删除操作,但是并没有真正的将堆顶元素移除。堆的删除操作后,只需要对堆顶元素进行下沉操作即可重新调整为大顶堆,而构建堆也是只用到下沉操作,因此堆排序的实现只需要用到堆的下沉操作。

Java 代码实现如下:

public class HeapSort {

    /**
     * 堆排序(升序)
     *
     * @param values 待排序数组
     */
    public static void heapSort(int[] values) {
        for (int i = (values.length - 2) / 2; i >= 0; i--) {
            downAdjust(values, i, values.length);
        }
        for (int i = values.length - 1; i > 0; i--) {
            int temp = values[0];
            values[0] = values[i];
            values[i] = temp;
            downAdjust(values, 0, i);
        }
    }

    /**
     * 堆节点下沉
     *
     * @param values 堆数组
     * @param index  下沉节点索引
     * @param length 堆调整的实际有效长度
     */
    static void downAdjust(int[] values, int index, int length) {
        int childIndex = 2 * index + 1;
        int temp = values[index];
        while (childIndex < length) {
            if (childIndex + 1 < length && values[childIndex + 1] > values[childIndex]) {
                childIndex++;
            }
            if (temp > values[childIndex]) {
                break;
            }
            values[index] = values[childIndex];
            index = childIndex;
            childIndex = 2 * index + 1;
        }
        values[index] = temp;
    }
}

算法分析

  • 堆排序是不稳定的
  • 堆排序的时间复杂度为O(nlogn)