十-4, 堆排序及其Java实现

前置知识:

堆:可以把堆看做一棵顺序存储的完全二叉树,这棵树满足,所有的子结点都不大于或不小于其父结点。
大顶堆:每个结点皆大于或等于其左右孩子结点; 一般升序采用大顶堆;
小顶堆:每个结点皆小于或等于其左右孩子结点; 一般降序采用小顶堆;

在这里插入图片描述

  • 由于完全二叉树的性质(只有最下面两层结点度小于2,并且最下一层叶结点集中在靠左的位置上),使得堆很容易用数组来表示,每个节点对应数组的一个元素, 拿上图一大顶堆为例得到下图.

在这里插入图片描述

  • 在数组地址从0开始时,所以我们可以从一个节点的下标i,得出这个节点的父节点下标为i/2-1 向下取整,这个节点的左孩子节点下标为2i+1,右孩子节点下标为2i+2,

  • 比如上图中, 40结点下标为2, 它的左孩子结点为 2i+1=5 即是35, 它的右孩子结点为 2i+2 =6 即是30.

  • 若下标从1开始,则某个节点的父节点下标为向下取整 i/2,左孩子节点为2i,右孩子节点为2i+1

稳定性:不稳定

在堆的调整过程中,比较和交换所走的是该结点到叶子结点的一条路径,对于相同关键字的结点,可能会出现排在后面的关键字被交换到前面的情况;

原理演示:

在这里插入图片描述

基本思想:

  1. 将待排序列构造成一个大顶堆;
  2. 此时, 整个序列的最大值就是堆顶的根节点;
  3. 将其与末尾元素进行交换, 此时末尾就是最大值;
  4. 然后将剩余的n-1个元素重新构造成一个堆, 这样会得到n个元素的次小值, 如此反复执行, 便能得到一个有序序列了;
  • 可以看到在构建大顶堆的过程中, 元素的个数逐渐减少, 最后得到一个有序的序列了.

*注:优先队列一般使用最小堆。

Java实现的具体步骤

  1. 首先我们需要一个调整堆为大顶堆或小堆顶的方法(adjustHeap(int[] arr, int length)), 在这个方法中一定要有的参数是, 存放堆的数组 arr, 以及数组的长度length, 为什么一定需要传入数组的长度呢? 因为每调整好一次堆, 我们就使用2中的方法, 把堆顶的元素(根节点)跟堆的最后一个结点进行了交换, 这些元素算是已经有序了的, 所以在这个过程中堆中的待排元素是慢慢减少了的;
  2. 我们还需要的一个方法是取出堆顶的元素(根节点)跟数组的尾部元素进行交换的反复 headpSort(int[] arr)
  3. 此外, 也切记要写一个数组中的两个元素进行交换的方法 arrSwap(int[] arr, int index1, int index2);

代码示例:

package DataStrcture.SortAlgorithmsDemo;

import java.util.Arrays;

public class HeapSort_copy {
/**
     * 堆排序
     * 1.先调整堆, 每次交换出一个元素后都要进行堆的调整, 或大顶堆, 或小顶堆
     * 2. 当调整为大顶堆(升序)或者小顶堆(降序), 把堆顶元素与堆中的最后一个元素进行交换.
     * 3. 每调整一次就交换一次, 最终整个序列都是有序的了
     */
     //程序结构
     // 1. 调整大顶堆/小顶堆的adjustHeap()
    //2. 把堆顶(根节点)结点交换到数组末尾的方法
    //3. 数组元素交换的工具方法
    
    //1

    /**
     * 
     * @param arr 待排数组(动态变化的, 去掉了末尾已经排好序的元素)
     * @param length 长度为arr.length-已排好序的元素个数
     */
    public static void adjustHeap(int[] arr, int length) {
        //对树中的非叶结点进行交换操作
        for (int i = length / 2 - 1; i >= 0; i--) {
            //找出非叶结点的左子节点2*i+1
            int k = 2 * i + 1;
            // 如果左子节点小于右子节点, 则k更新
            //
            if (k + 1 < length && arr[k] < arr[k + 1]) {
                k++;
            }
            //比较非叶结点和左/右子节点
            if (arr[i] < arr[k]) {
                swapArr(arr, i, k);
            }
        }
    }

    // 交换元素到数组的尾部
    public static void heapSort(int[] arr) {
        for (int i = arr.length; i > 1; i--) {
            adjustHeap(arr, i);
            swapArr(arr, 0, i - 1);
        }
    }

    public static void swapArr(int[] arr, int index1, int index2) {
        int temp = arr[index1];
        arr[index1] = arr[index2];
        arr[index2] = temp;
    }

    //测试方法
    public static void main(String[] args) {
        int[] arr = {4, 6, 8, 5, 9, 10, 3};
        heapSort(arr);
        System.out.println(Arrays.toString(arr));
    }
}

在这里插入图片描述

posted @ 2022-05-26 20:31  青松城  阅读(46)  评论(0编辑  收藏  举报