数据结构---堆排序


二叉堆的构建,删除,调整是实现堆排序的基础
之前博客写了二叉堆: 二叉堆

  1. 最大堆的堆顶是整个堆中的最大元素。
  2. 最小堆的堆顶是整个堆中的最小元素。

堆排序步骤:

  1. 把无序数组构建成二叉堆。(需要从小到大排序,则构建成最大堆;需要从大到小排序,则构建成最小堆。)
  2. 循环删除堆顶元素,替换到二叉堆的末尾,调整堆产生新的堆顶。

最后删除的元素依次组成了有序的数列。

第一步:删除一个最大堆的堆顶(并不是完全删除,而是跟末尾的节点交换位置)
第二步:再过自我调整,第2大的元素就会被交换上来,成为最大堆的新堆顶

在这里插入图片描述
先删除堆顶元素(跟末尾的节点交换位置)
在这里插入图片描述
再自我调整(第2大的元素就会被交换上来)
在这里插入图片描述
由于二叉堆的这个特性,每一次删除旧堆顶,调整后的新堆顶都是大小仅次于旧堆顶的节点。那么只要反复删除堆顶,反复调整二叉堆,所得到的集合就会成为一个有序集合:

删堆顶9,8变成新堆顶
在这里插入图片描述
删除节点8,节点7成为新堆顶。
在这里插入图片描述
在这里插入图片描述
。。。。循环往复
在这里插入图片描述
删除3,
在这里插入图片描述

原本的最大二叉堆已经变成了一个从小到大的有序集合。
二叉堆实际存储在数组中,数组中的元素排列如下
在这里插入图片描述

JAVA实现

package mysort.heapSort;

import java.util.Arrays;

//构建最大堆
public class heapSort {

    /**
     * “下沉”调整(删除元素)
     * @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];
            //再向下找,直到childIndex>=length,结束
            parentIndex = childIndex;
            childIndex = 2*childIndex+1;
        }
        //最后再把初始的父节点值赋值过去
        array[parentIndex]=temp;
    }

    /**
     * 构建最大堆
     * @param array 待调整的堆
     */
    public static void buildHeap(int[] array){
        // 从最后一个非叶子节点(array.length-2)/2)开始,依次做“下沉”调整
        for (int i = (array.length-2)/2;i>=0;i--){
            downAdjust(array,i,array.length);
        }
    }


    /**
     * 用最大堆排序(升序)
     * @param array  待调整的堆
     */
    public static void heapSort(int[]array){
        // 1. 把无序数组构建成最大堆
        buildHeap(array);
        System.out.println("构建成功,最大堆是:" + Arrays.toString(array));
        //执行堆排序(把堆顶元素删除,放到末尾)
        for (int i =array.length-1;i>0;i--){
            // 最后1个元素和第1个元素进行交换
            int temp = array[i];
            array[i] = array[0];
            array[0] = temp;
            //再把堆顶元素下沉,调整最大堆
            //这里,注意:i
            //随着i--.之前的最后的几位就保存排序好的数字了,就不动了
            downAdjust(array,0,i);
        }
    }
    public static void main(String[] args) {
        int[] arr = new int[]{1,3,2,6,5,7,8,9,10,0};
        heapSort(arr);
        System.out.println("堆排序之后的结果为: "+Arrays.toString(arr));
    }
}

在这里插入图片描述

最大堆排序(升序排序)

空间复杂度是O(1)
时间复杂度:

  1. 把无序数组构建成二叉堆,这一步的时间复杂度是O(n)。
  2. 循环删除堆顶元素,并将该元素移到集合尾部,调整堆产生新的堆顶。需要进行n-1次循环。每次循环调用一次downAdjust方法,所以第2步的计算规模是 (n-1)×logn ,时间复杂度为O(nlogn)。(二叉堆的节点“下沉”调整(downAdjust 方法)是堆排序算法的基础,时间复杂度是O(log n)。)
  3. 两个步骤是并列关系,所以整体的时间复杂度是O(nlogn)。

和快速排序区别

堆排序和快速排序的平均时间复杂度都是O(nlogn),并且都是不稳定排序。
至于不同点,快速排序的最坏时间复杂度是O(n2),而堆排序的最坏时间复杂度稳定在O(nlogn)。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值