堆排序详解

堆排序详解

说明

  1. 堆排序基于堆的特性,速度较快,效率较高,平均时间复杂度为线性对数阶
  2. 先说明大顶堆和小顶堆
  3. 大顶堆是指当前二叉树的父节点对应的值总是大于子节点对应的值,及arr[i] >= arr[ 2 * i + 1] 并且arr[i] >= arr[ 2 * i + 2]总成立,不论是根节点还是其子树
  4. 小顶堆刚好相反,其父节点对应的值全部小于等于子节点
  5. 而堆排序只是基于大顶堆和小顶堆来实现,具体使用哪个依赖于升序排序还是降序排序,如果是升序则使用大顶堆,如果是降序则使用小顶堆
  6. 堆排序实际操作的并不是二叉树,而是数组,因为二叉树可以顺序存储,及使用数组来模拟二叉树,当前节点的子节点对应索引为 2 * i + 1 和 2 * 1 +2
  7. 要完成堆排序,首先需要将一颗完全二叉树调整成一个大顶堆,因为大顶堆的根节点是数组元素中最大的,再将这个根节点对应的元素和数组中的最后一个元素进行交换位置,然后对数组中剩余的 n - 1个元素再调整成为大顶堆,再交换位置,依次进行,直到数组中元素全部调整完
  8. 完全二叉树调整大顶堆思路:
  9. 假设数组长度为len , 当前要调整的子树对应的节点在数组中索引为 i ,先判断当前节点的左右子节点的大小,让子节点中较大的数和当前节点比较,如果子节点的数大于当前节点,说明需要交换
  10. 交换完成后,重置 i ,然后循环调整
  11. 实现调整大顶堆的方法后,按照总左到右,从下到上的顺序将当前二叉树进行大顶堆的调整
  12. 即先找到最左下角的非叶子节点开始调整,依次循环,直到调整到数组的第一个元素为止
  13. 第一次调整完后,交换数组的第一个元素和最后一个元素,即将最大的元素移动到数组的最后
  14. 然后重置数组的大小,即下次调整时数组长度 - 1,让最大的数不参与调整,将剩下的数调整完后再交换顺序
  15. 源码及分析如下

源码及分析

package algorithm.tree;

import java.util.Arrays;

/**
 * @author AIMX_INFO
 * @version 1.0
 */
public class HeapSort {
    public static void main(String[] args) {
        System.out.println("堆排序...");
        int[] arr = {2, 1, 45, 34, 77, 23};
        heapSort(arr);

    }

    public static void heapSort(int[] arr) {
        //定义临时变量tmp用于交换
        int tmp = 0;
        //按照从左到右,从下到上的顺序,依次调整大顶堆
        for (int i = arr.length / 2 - 1; i >= 0; i--) {
            adjustHeap(arr, i, arr.length);
        }
        //将调整后的大顶堆中的最大值移动到数组的最后
        for (int i = arr.length - 1; i > 0 ; i--) {
            tmp = arr[i];
            arr[i] = arr[0];
            arr[0] = tmp;
            adjustHeap(arr,0,i);
        }
        System.out.println(Arrays.toString(arr));

    }

    //编写核心的调整大顶堆方法
    /**
     * @param arr    要调整的数组
     * @param i      非叶子节点的索引
     * @param length 数组元素长度
     */
    public static void adjustHeap(int[] arr, int i, int length) {
        //定义变量保存要调整的非叶子节点的元素
        int tmp = arr[i];
        //循环调整数组,将其调整为一个大顶堆
        for (int k = 2 * i + 1; k < length; k = k * 2 + 1) {
            //判断当前非叶子节点左右节点大小,让k指向当前非叶子节点的较大节点索引
            if (k + 1 < length && arr[k] < arr[k + 1]) {
                k++;
            }
            //如果当前叶子节点的值大于非叶子节点的值,则交换顺序
            if (arr[k] > tmp) {
                arr[i] = arr[k];
                //并将 k 指向当前较大的节点,开始下一次循环比较
                i = k;
            } else {
                break;
            }
            //for循环结束后,则以 i 为根的子树已经成为一颗大顶堆数
            arr[i] = tmp;
        }
    }
}

posted @ 2021-06-07 10:47  mx_info  阅读(169)  评论(0)    收藏  举报