二叉树的应用——堆排序

想一件事从来都不难,难就难在我们是否真的有毅力和决心去做成这件事。

                                                                        ——致不断努力前进的我自己

在了解算法逻辑后,不断地去推演代码,总结发现的问题,最后解决问题。

                                                                        ——学习终告


文章难免会有不全面的的地方,后续会在复习时不断改进,补充。(查阅资料后笔记总结)


什么是堆排序?

      堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。堆排序可以说是一种利用堆的概念来排序的选择排序。

      分为两种方法:

  1. 大顶堆:每个节点的值都大于或等于其子节点的值,在堆排序算法中用于升序排列arr[i]>=arr[i*2+1]&&arr[i]>=arr[i*2+2];
  2. 小顶堆:每个节点的值都小于或等于其子节点的值,在堆排序算法中用于降序排列arr[i]<=arr[i*2+1]&&arr[i]<=arr[i*2+2];

黄色区域在后面会进行分析与解释; 


 堆排序操作的对象是什么?

我们在了解了堆排序的底层实现后,可能就会发出这样的疑问,堆排序的操作对象是什么?我们在分析堆排序的流程是是在完全二叉树上推导的,但是操作的对象却是是数组,原因:我们的二叉树是可以通过顺序化,存储到数组里的;


堆排序代码思路:

逻辑思路引导可参考哔哩哔哩视频教程:

【堆排序】https://www.bilibili.com/video/BV12f4y1j78Rvd_source=7fc25e69b07a8ca7953568a66105e30d

递归运算堆的基本排序步骤为:

  1. 首先创建一个方法表示堆排序,参数为待排序数组
  2. 在排序之前,我们首先要对当前数组操作使其变成一个大顶堆或者小顶堆,然后整棵树的最大值(最小值)就在堆顶(数组的0索引处),将最大值(最小值)交换到数组的最后。
  3. 然后递归迭代前(n-1)个数,继续构造大顶堆或者小顶堆,依次迭代,直到数组剩下长度为1(arr.length==1);

那么如何建立最大堆呢(最小堆相反比较即可):

  1. 因为只需要对非叶子节点做大顶堆构建,所以建大顶堆的操作实际上是[0, n/2-1]之间的节点。
  2. 对序号为i的节点做最大堆的构建,那么该节点的左子节点为:i*2-1, 右子节点为:i*2
  3. 需要检验左右子节点是否存在,存在就进行判断,将最大值换到树顶。然后继续递归构造大顶堆。
  4. 每当我们发现构造的当前堆的根节点发生变更时,需要再次对其父节点做最大堆的重新构造。

我们可以发现,堆算法巧妙的运用了数组和二叉树数据结构关系以及堆数据结构的性质来实现的。只要你懂得大顶堆、小顶堆的性质,以及如何构建一个大顶堆和小顶堆,那么该算法应该很快能实现


关键组成部分分析:

 在数组中索引i处的元素对应到二叉树中,那么他的左右子节点的索引是什么呢?我们直接记结论:

 索引 处的元素对应的左子节点索引为i*2+1、右子节点为i*2+2;

 在一个完全二叉树中我们如何通过对应数组中的索引去的到他的最后一个非叶节点的索引值?(在堆排序中我们是从最后一个非叶节点开始处理第一步操作的(对应代码中的第一个for循环的作用,下面有注释))我们直接记结论:

最后一个非叶节点的索引值为arr.length/2-1;

代码实现:

public class AdjustHeap {
    public static void main(String[] args) {
        int[] arr = {1, 4, 6, 8, 9, 7};
        for (int i = arr.length/2-1; 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);
        }//这个循环是将,将去掉上面的最大值后,对剩余元素进行沉淀最大值到最后
        print(arr);//这是一个遍历数组的方法
  public static void adjustheap(int[] arr, int i, int length) {
        int temp = arr[i];
        //记录当前节点值,用于在(**)标记处交换。
        for (int j = i * 2 + 1; j < length; j = j * 2 + 1) {
            if (j + 1 < length && arr[j] < arr[j + 1]) {
                j++;
            }
            if (arr[i] < arr[j]) {
                arr[i] = arr[j];
                i = j;
            }
        }
        arr[i] = temp;//(**)
    }

总结

提示:这只是我自己的笔记记录,对于堆排序的理解和解释:可能会有错误,请慎重参考。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值