堆排序

  堆排序(英语:Heapsort)是指利用堆这种数据结构所设计的一种排序算法。是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序

  堆,是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。如果每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。如下图:

  

同时,我们对堆中的结点按层进行编号,将这种逻辑结构映射到数组中就是下面这个样子:

 

该数组从逻辑上讲就是一个堆结构,我们用简单的公式来描述一下堆的定义就是:

大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2] 

小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]

计算某一节点其孩子节点的数组下标

我们习惯性的采用数组这一数据结构来记录一系列有序或者无序的数据。

Eg:   Int[] A = {453261}

 

   综合数组和二叉树解结构来看,我们可以通过某一数字在数组中的下标,计算出二叉树中该数字的左右子节点,或者父节点在数组中的下标。以数字5为例,它在数组中的取值为A[1],根据二叉树,其父节点为4,即A[0];同时其左节点为2,右节点为6,分别对应数组中的A[3]A[4]

  故可得出一个结论,A[i]的左节点为A[2i+1],右节点为A[2i+2]如果A[i]为右子节点则,其父节点为A[i/2-1],如果A[i]为左子节点则其父节点为A[(i-1)/2],它从数组索引的角度描述了数字与数字在二叉树中的位置关系。此结论实际上是完全二叉树的一个基本性质,这也是为什么堆的结构性要求其满足完全二叉树的形式,就是为了使用此结论。

堆排序的基本思想步骤

堆排序的基本思想是:将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,自上而下,自左向右进行调整。

具体思想步骤待补充。

代码如下

public class HeadSort {

    public static void main(String[] args) {
        int[] arr = {4, 5, 3, 2, 6, 1, 5};
        heapSort(arr);
        System.out.println("排序后结果为:" + Arrays.toString(arr));

    }

    public static void heapSort(int[] arr) {
        int n = arr.length;
        //创建大根堆(根节点元素是最大),这里元素的索引是从0开始的,所以最后一个非叶子结点的数组下标为array.length/2 - 1
        for (int i = n / 2 - 1; i >= 0; i--) {
            heapify(arr, n, i);
        }

        //调整大根堆,目的只有一个:将较大元素调整至末尾处!
        for (int j = n - 1; j > 0; j--) {
            //首位交换,交换堆的根结点(最大元素)与当前最后一个元素(i)
            int temp = arr[0];
            arr[0] = arr[j];
            arr[j] = temp;

            //堆化操作,自上而下,自左向右进行调整的
            //元素交换之后,毫无疑问,最后一个元素无需再考虑排序问题了。在去掉最后一个元素的堆上进行堆化操作,这也是为什么此方法放在循环里的原因
            heapify(arr, j, 0);
        }
    }

    /**
     * 堆化操作
     * @param arr 数组
     * @param n 数组长度
     * @param i 父节点数组下标
     */
    static void heapify(int[] arr, int n, int i) {
        // 初始化最大元素的数组下标
        int largest = i;
        // 数组下标为i的左孩子结点的下标为l = 2*i + 1
        int l = 2 * i + 1;
        // 数组下标为i 的右孩子结点的下表为r = 2*i + 2
        int r = 2 * i + 2;

        // 如果左孩子结点比父结点大,更新largest为左孩子下标
        if (l < n && arr[l] > arr[largest]) {
            largest = l;
        }

        // 如果右孩子比最大元素大,更新largest为右孩子下标
        if (r < n && arr[r] > arr[largest]) {
            largest = r;
        }

        // 如果最大元素不是根结点,进行交换操作并递归调用Heapify
        if (largest != i) {
            int swap = arr[i];
            arr[i] = arr[largest];
            arr[largest] = swap;

            //对由于交换操作受到影响的子树递归调用Heapify
            heapify(arr, n, largest);
        }
    }
}

希望本文章对您有帮助,您的转发、点赞是我的创作动力,十分感谢。更多好文推荐,请关注我的微信公众号--JustJavaIt
原文地址:https://www.cnblogs.com/liaowenhui/p/15759587.html