欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

排序8-堆排序

程序员文章站 2022-03-18 09:40:42
...

        利用堆这种数据结构来进行排序的算法, 堆是一种优先队列,它有两种实现,最大堆和最小堆. 

       堆(以下全都默认为最大堆)看成一棵完全二叉树,但是位于堆顶的元素总是整棵树的最大值,每个子节点的值都比父节点小,由于堆要时刻保持这样的规则特性,所以一旦堆里面的数据发生变化,我们必须对堆重新进行一次构建。

  既然堆顶元素永远都是整棵树中的最大值,那么我们将数据构建成堆后,第一次取的元素,就是最大值?取完后把堆重新构建一下,然后再取堆顶的元素,取的就是第二大的值, 反复的取,取出来的数据也就是有序的数据。

1. 示意图: 

排序8-堆排序

以[8,2,5,9,7,3]这组数据来演示. 

首先,将数组构建成堆。
 

排序8-堆排序

接下来,我们取出堆顶的数据,也就是数组第一个数,9,取法是将数组的第一位和最后一位调换,然后将数组的待排序范围-1,

排序8-堆排序

现在的待排序数据是[3,8,5,2,7],我们继续将待排序数据构建成堆。
 

排序8-堆排序

取出堆顶数据,这次就是第一位和倒数第二位交换了,因为待排序的边界已经减1。

排序8-堆排序

继续构建堆

排序8-堆排序

从堆顶取出来的数据最终形成一个有序列表. 

2. 流程描述:

  • 将初始待排序关键字序列(R1,R2….Rn)构建成大顶堆,此堆为初始的无序区;
  • 将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,……Rn-1)和新的有序区(Rn),且满足R[1,2…n-1]<=R[n];
  • 由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,……Rn-1)调整为新堆,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2….Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为n-1,则整个排序过程完成。

3. java代码实现. 

    /**
     * 堆排序
     * @param arr 排序数组
     */
    public static void sort(int[] arr) {

        int length = arr.length;

        //构建堆
        buildHeap(arr, length);

        for (int i = length - 1; i > 0; i--) {
            //将堆顶元素与末位元素调换
            int temp = arr[0];
            arr[0] = arr[i];
            arr[i] = temp;

            //数组长度-1 隐藏堆尾元素
            length--;

            //将堆顶元素下沉 目的是将最大的元素浮到堆顶来
            sink(arr, 0, length);
        }
    }

    /**
     * 构建堆
     * @param arr 数组
     * @param length 数组范围
     */
    private static void buildHeap(int[] arr, int length) {
        for (int i = length / 2; i >= 0; i--) {
            sink(arr, i, length);
        }
    }

    /**
     * 下沉调整
     * @param arr 数组
     * @param index 调整位置
     * @param length 数组范围
     */
    private static void sink(int[] arr, int index, int length) {
        int leftChild = 2 * index + 1;//左子节点下标
        int rightChild = 2 * index + 2;//右子节点下标
        int present = index;//要调整的节点下标

        //下沉左边
        if (leftChild < length && arr[leftChild] > arr[present]) {
            present = leftChild;
        }

        //下沉右边
        if (rightChild < length && arr[rightChild] > arr[present]) {
            present = rightChild;
        }

        //如果下标不相等 证明调换过了
        if (present != index) {
            //交换值
            int temp = arr[index];
            arr[index] = arr[present];
            arr[present] = temp;

            //继续下沉
            sink(arr, present, length);
        }
    }

3. 分析:   堆排序的时间复杂度是O(nlogn)。

相关标签: 算法与数据结构