数据结构:排序之堆排序Heap Sort

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_40042143/article/details/87929016

Heap Sort,堆排序,对简单选择排序的一种改进

二叉堆:顺序存储

堆具有完全二叉树的性质:每个结点值都大于或等于其左右孩子结点的值,成为大顶堆;或者每个结点值都小于等于其左右孩子结点值,称为小顶堆。

                                                                           大顶堆,图片来自程序员小辉灰  

                                                                        小顶堆,图片来源于程序员小灰

对于n个元素的序列{R_{0},R_{1},...,R_{n}}当且仅当满足下列关系之一时,称之为堆

(1)R_{i}<=R_{2i+1}R_{i}<=R_{2i+2} 【小根堆】

(2)R_{i}>=R_{2i+1}R_{i}>=R_{2i+2} 【大根堆】

其中,i=0,1,2,...,n/2向下取整

堆的自我调整

有一下几种操作:

插入结点:二叉堆插入结点位置在完全二叉树的最后一个位置

删除结点:删除过程和插入过程相反,删除的是堆顶的元素

构建二叉堆:把一个无序的完全二叉树调整为二叉堆,本质,让所有非叶子节点依次下沉

二叉堆顺序存储在数组里,如下图,计算要依靠数组下标

   

                                                   图片来于程序员小灰

堆排序基本思想:将待排的序列构成一个大顶堆,整个序列的最大值就是堆顶元素-根结点。将它移走(与堆数组的末尾元素交换),然后将剩余的n-1个序列重新构成一个堆,得到n个元素中次大值,如此反复执行,得到有序序列。

堆排序算法步骤

  1. 把无序数组构建成二叉堆
  2. 循环删除对顶元素,移到集合尾部,调节堆产生新的堆顶
    /**
     * 堆排序
     */
    public static void heapSort(int[] array) {
        //1.把无序数组构造位二叉堆
        for (int i = (array.length - 2) / 2; i >= 0; i--) {
            downAdjust(array, i, array.length);
        }
        System.out.println(Arrays.toString(array));

        //循环删除堆顶元素,移到集合尾部,调节堆产生新堆
        for (int i = array.length - 1; i > 0; i--) {
            //最后一个和第一个元素交换
            int temp = array[i];
            array[i] = array[0];
            array[0] = temp;

            //下沉调整最大堆
            downAdjust(array, 0, i);
        }
    }

将待排序的序列构造成大顶堆,本质,从下往上,从右到左,将内阁非叶节点当作根结点,将其和其子树调整位大顶堆----注意i的变化是在调整那些元素哦

    /**
     * 下沉调整
     *
     * @param array       待调整的堆
     * @param parentIndex 要下沉的父节点
     * @param length      堆的有效大小
     */

    public static void downAdjust(int[] array, int parentIndex, int length) {
        //temp保存父节点值
        int temp = array[parentIndex];
        int childIndex = 2 * parentIndex + 1;
        while (childIndex < length) {
            //如果有右孩子且右孩子小于左孩子,则定位到右孩子
            if (childIndex + 1 < length && array[childIndex + 1] < array[childIndex]) {
                childIndex++;

            }
            //如果父节点小于任何一个孩子的值,直接跳出
            if (temp <= array[childIndex]) {
                break;
            }
            array[parentIndex] = array[childIndex];
            parentIndex = childIndex;
            childIndex = 2 * childIndex + 1;
        }
        array[parentIndex] = temp;

    }

复杂度分析

第一步,把无序数组构建成二叉堆,时间复杂度位O(n)

第二步,需要进行n-1次循环,每次循环调用依次downAdjust,时间复杂度位O(nlogn)

两个步骤并列关系,所以总的时间复杂度:O(nlogn)

堆排序最好,最差,平均的时间复杂度均为O(nlogn)

空间复杂度:需要一个辅助存储单元

猜你喜欢

转载自blog.csdn.net/weixin_40042143/article/details/87929016