自己还不会的堆排序

自己还不会的堆排序

之前堆排序我只听说过,但是还一直没有学到,这次来学习一下。

堆排序

定义

百度百科给出的定义:是指利用这种数据结构所涉及的一个算法…(这不没说吗)

那我们就来说说堆是什么吧

:堆通常是一个可以被看做一颗完全二叉树的数组对象,它的特点是子节点的键值或索引总是小于等于(或大于等于)它的父节点。(这么说就是要么一定比父节点及其直属关系(我发明的词懂就好)的节点大或小)

根节点最大的堆叫最大堆或大根堆,根节点最小的堆叫最小堆或小根堆,对左右子节点的大小关系没有要求。
在这里插入图片描述

分析

大致原理:利用大根堆或小根堆思想,首先建立堆,然后将堆首与堆尾交换,堆尾之后为有序区

构建堆中有上浮下沉的概念
上浮:如果一个节点比父节点大,那么要交换两个节点;交换后可能比它新的父节点还大,因此还要不断进行比较和交换,把这种称为上浮

下沉:类似的,一个结点比原来子节点小,就向下比较和交换,称为下沉;一个结点若有两个子节点,应与最大子节点进行交换

  1. 构建初始堆,将待排序列构建成一个大根堆(或小根堆),想要结果升序就构建大根堆,想要降序就构建小根堆(后面要与末位交换)
  2. 此时,序列的的最大值即为堆顶的根节点
  3. 将堆顶元素与当前堆末尾元素交换,并断开(从待排序列中移除)堆尾元素,(相当于最后的就是已经排好序的部分了,后面再操作就是对尚未排序的部分进行构建堆)此时末尾就是最大值
  4. 重新构建堆
  5. 重复2~3,直到待排序列中只剩下一个元素(堆顶元素)

这样最主要的开销就在于每次对于待排序序列的堆构建过程,将根节点和堆尾元素互换只是每次构建堆之后顺手的事(但是也很重要),构建堆的时间复杂度是O(nlogn),故堆排序的时间复杂度也为O(nlogn)

下面是图片的演示,图上是树的形式,但是我们是用数组存储的,是完全二叉树的层次遍历数组。
在这里插入图片描述
这是GIF动图演示

在这里插入图片描述

代码

这里直接复用了这篇博客的代码

/**
 * @ClassName HeapSort
 * @Description 堆排序
 * @author 滑技工厂 https://blog.csdn.net/qq_41718454
 * @date 2021/2/4
 * @version 1.0
 */
public class HeapSort {
    /*
     * 堆排序:此处以升序排序
     * 时间复杂度为O(nlogn)
     * 堆:是一种满足一下条件的完全二叉树
     *   1.每个节点的值都大于或者等于其左右子节点的值称之为大顶堆;
     *   2.每个节点的值都小于或者等于其左右子节点的值称之为小顶堆;
     *   3.对于节点的左右子节点的大小关系没有做任何要求;
     * 基本思想:
     * 1)将待排序的序列构造成一个大顶堆----大顶堆利用前面的顺序存储二叉树的数组结构存储
     * 2)此时,序列的的最大值即为堆顶的根节点
     * 3)将其与末尾的元素进行交换,此时,末尾就是最大值
     * 4)然后再将n-1个元素重新进行构成一个大顶堆,这样可以得到序列的次小值,反复执行,便可以得到一个有序序列
     * 5)最终得到升序排列的有序序列
     */
    public static void main(String[] args) {
//        int[] arr = {5, 1, 7, 3, 1, 6, 9, 4};
        int[] arr = {16, 7, 3, 20, 17, 8};

        heapSort(arr);

        for (int i : arr) {
            System.out.print(i + " ");
        }
    }


    /**
     * 创建堆,
     *
     * @param arr 待排序列
     */
    private static void heapSort(int[] arr) {
        //创建堆
        for (int i = (arr.length - 1) / 2; i >= 0; i--) {
            //从第一个非叶子结点从下至上,从右至左调整结构
            adjustHeap(arr, i, arr.length);
        }

        //调整堆结构+交换堆顶元素与末尾元素
        for (int i = arr.length - 1; i > 0; i--) {
            //将堆顶元素与末尾元素进行交换
            int temp = arr[i];
            arr[i] = arr[0];
            arr[0] = temp;

            //重新对堆进行调整
            adjustHeap(arr, 0, i);
        }
    }

    /**
     * 调整堆
     *
     * @param arr    待排序列
     * @param parent 父节点
     * @param length 待排序列尾元素索引
     */
    private static void adjustHeap(int[] arr, int parent, int length) {
        //将temp作为父节点
        int temp = arr[parent];
        //左孩子
        int lChild = 2 * parent + 1;

        while (lChild < length) {
            //右孩子
            int rChild = lChild + 1;
            // 如果有右孩子结点,并且右孩子结点的值大于左孩子结点,则选取右孩子结点
            if (rChild < length && arr[lChild] < arr[rChild]) {
                lChild++;
            }

            // 如果父结点的值已经大于孩子结点的值,则直接结束
            if (temp >= arr[lChild]) {
                break;
            }

            // 把孩子结点的值赋给父结点
            arr[parent] = arr[lChild];

            //选取孩子结点的左孩子结点,继续向下筛选
            parent = lChild;
            lChild = 2 * lChild + 1;
        }
        arr[parent] = temp;
    }
}

堆排序的思想大概就是这样,如果有什么不同的请大家评论区指出,谢谢!!!

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值