经典排序之堆排序

堆排序思路

堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。堆排序可以说是一种利用堆的概念来排序的选择排序。分为两种方法:
大顶堆:每个节点的值都大于或等于其子节点的值,在堆排序算法中用于升序排列;
小顶堆:每个节点的值都小于或等于其子节点的值,在堆排序算法中用于降序排列;

二叉树的性质

已知父节点的下标,可以获取其子节点的下标

父节点下标为 i
左子树下标为 i x 2 + 1
右子树下标为 i x 2 + 2
例: i = 1 左为3 右为4
同理知道左右子树可以知道父节点下标。

思路

根据大顶堆可以知道,root节点大于所有节点为最大,那么我们可以将其移动数组最后一位,然后将 [0, n-1] 进行堆的构建 循环调用

代码展示

 public int[] sortArray(int[] arr) {
        int len = arr.length;
        // 初始化构建
        buildMaxHeap(arr, len);
        // 构建之后 0下标为数组最大值,for循环从最后一位开始,交换 然后重新构建
        for (int i = len - 1; i > 0; i--) {
            swap(arr, 0, i);
            len--;
            heapify(arr, 0, len);
        }
        return arr;
    }
    // 对半构建
    private void buildMaxHeap(int[] arr, int len) {
        for (int i = len >> 1; i >= 0; i--) {
            heapify(arr, i, len);
        }
    }

    private void heapify(int[] arr, int i, int len) {
        // len 为本次构建堆的数组长度
        // 获得父节点 左子树,右子树 下标位置
        int left = 2 * i + 1;
        int right = 2 * i + 2;
        int largest = i;
        // 左子树下标小于 数组长度 并且 左子树的值大于父节点 则改变largest 标识位
        if (left < len && arr[left] > arr[largest]) {
            largest = left;
        }
        // 右子树与左子树相同
        // 这里如果左子树时已经改变 则比较左右子树大小
        if (right < len && arr[right] > arr[largest]) {
            largest = right;
        }
        // 如果父节点标识为发生改变,则将标识为指向的位置与父节点进行交换,并重新构建
        if (largest != i) {
            swap(arr, i, largest);
            // 现在发生改变,则查看改变之后的位置的变化
            heapify(arr, largest, len);
        }
    }
    private void swap(int[] arr, int i, int j) {
        int next = arr[i];
        arr[i] = arr[j];
        arr[j] = next;
    }
posted @ 2022-08-17 13:04  SpoonBlog  阅读(39)  评论(0)    收藏  举报