排序-堆排序

简介

堆排序(Heapsort)是指利用 二叉堆 这种数据结构所设计的一种排序算法。堆排序的适用数据结构为数组。

工作原理

本质是建立在堆上的选择排序。

排序过程

首先建立大顶堆,然后将堆顶的元素取出,作为最大值,与数组尾部的元素交换,并维持残余堆的性质;

之后将堆顶的元素取出,作为次大值,与数组倒数第二位元素交换,并维持残余堆的性质;

以此类推,在第 n − 1 n-1 n1次操作后,整个数组就完成了排序。

在数组上建立二叉堆

从根节点开始,依次将每一层的节点排列在数组里。

于是有数组中下标为 i i i 的节点,对应的父结点、左子结点和右子结点如下:

iParent(i) = (i - 1) / 2;
iLeftChild(i) = 2 * i + 1;
iRightChild(i) = 2 * i + 2;

性质

稳定性

同选择排序一样,由于其中交换位置的操作,所以是不稳定的排序算法。

时间复杂度

堆排序的最优时间复杂度、平均时间复杂度、最坏时间复杂度均为 O ( n log ⁡ ( n ) ) O(n\log(n)) O(nlog(n))

空间复杂度

而由于可以在输入数组上建立堆,所以这又是一个原地算法。

代码

大顶堆

	public static void heapSort(int[] arr) {
        // 构建初始大顶堆
        buildMaxHeap(arr);
        for (int i = arr.length - 1; i > 0; i--) {
            // 将最大值交换到数组最后
            swap(arr, 0, i);
            // 调整剩余数组,使其满足大顶堆
            maxHeapify(arr, 0, i);
        }
    }
    /**
     * 构建初始大顶堆
     *
     * @param arr
     */
    public static void buildMaxHeap(int[] arr) {
        // 从最后一个非叶子结点开始调整大顶堆,最后一个非叶子结点的下标就是 arr.length / 2-1
        for (int i = arr.length / 2 - 1; i >= 0; i--) {
            maxHeapify(arr, i, arr.length);
        }
    }

    /**
     * 调整大顶堆
     *
     * @param arr
     * @param i
     * @param heapSize 剩余未排序的数字的数量,也就是剩余堆的大小
     */
    public static void maxHeapify(int[] arr, int i, int heapSize) {
        // 左子结点下标
        int l = 2 * i + 1;
        // 右子结点下标
        int r = l + 1;
        // 记录根结点、左子树结点、右子树结点三者中的最大值下标
        int largest = i;
        // 与左子树结点比较
        if (l < heapSize && arr[l] > arr[largest]) {
            largest = l;
        }
        // 与右子树结点比较
        if (r < heapSize && arr[r] > arr[largest]) {
            largest = r;
        }
        if (largest != i) {
            // 将最大值交换为根结点
            swap(arr, i, largest);
            // 再次调整交换数字后的大顶堆
            maxHeapify(arr, largest, heapSize);
        }
    }

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

小顶堆

	
    /**
     * 构建初始小顶堆
     *
     * @param arr
     */
    private static void buildMinHeap(int[] arr) {
        // 从最后一个非叶子结点开始调整小顶堆,最后一个非叶子结点的下标就是 arr.length / 2-1
        for (int i = arr.length / 2 - 1; i >= 0; i--) {
            minHeapify(arr, i, arr.length);
        }
    }

    /**
     * 调整小顶堆
     *
     * @param arr
     * @param i
     * @param heapSize 剩余未排序的数字的数量,也就是剩余堆的大小
     */
    private static void minHeapify(int[] arr, int i, int heapSize) {
        // 左子结点下标
        int l = 2 * i + 1;
        // 右子结点下标
        int r = l + 1;
        // 记录根结点、左子树结点、右子树结点三者中的最小值下标
        int smallest = i;
        // 与左子树结点比较
        if (l < heapSize && arr[l] < arr[smallest]) {
            smallest = l;
        }
        // 与右子树结点比较
        if (r < heapSize && arr[r] < arr[smallest]) {
            smallest = r;
        }
        if (smallest != i) {
            // 将最小值交换为根结点
            swap(arr, i, smallest);
            // 再次调整交换数字后的小顶堆
            minHeapify(arr, smallest, heapSize);
        }
    }


    private static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值