堆排序(二叉树)

闲来无事,在网上看了一两个算法,感觉涨见识了,一个是快速排序,一个是堆排序。快速排序很好理解,看完感觉到了算法的魅力,然后是堆排序,我简单看了下的时候,感觉也很有魅力,但是之间的步骤的来由有点迷糊,所以琢磨了下。
 

堆排序原理:1.什么是“堆”,这里的“堆”是二叉树,如下图:

大致样式是这样的数据填于格子中。有“下级”格子的格子称为非叶子结点,没有“下级”格子的格子成为叶子结点。

比如图中最后一层的都是叶子j结点,第二层的最后一个是叶子结点,其他的都是节点。而且一个结点下最多有两个结点,因此称之为二叉树

堆分为大顶堆和小顶堆。大顶堆顾名思义,就是结点里的数据比他们的两个或一个子结点的数据都大,比如:3结点下面可以连着2和1。小顶堆则反之。因此大顶堆的最上面的结点的数据是最大的,小顶堆最上面的结点的数据是最小的。

如何排序:

1.将乱序的数组构建成一个堆,比如要升序排列则构建大顶堆,要降序排列则构建小顶堆。

2.将构建好的二叉树的顶部结点放置于数组的最后一位,然后排除最后一位重新整理堆,然后再将顶部结点放置倒数第二位再重新整理堆以此类推,知道堆没了。(就是每次构建之后顶部就是最大的,下一次的顶部就是次大的,依次来重新构建了数组)

 

问题1.如何构建堆

在构建堆时,堆这个结构在代码里是没有具体的表现出来的,而是再脑海里将数组角标依次填入二叉树中的。

比如:要排序的数组arr是 [3,5,2,4,8,6,1,7,9]

那么对应的二叉树是

排列顺序是从上至下,从左至右,依次排列。

 

问题2.如何进行第一步,将这个无序的二叉树变成大顶堆(这里我以大顶堆为例,小顶堆一样的)

整理是从下往上,从右至左依次整理各个非叶子结点,比如这里从4这个格子开始整理,将4和下面的7与9把最大的往上调,就变成了如下图:

第一个非子叶结点调整完之后调整下一个,就是上一层的右边的2结点,一样,把2,6,1中最大的换上去,也就是6和2互换,如下图

然后继续找下一个,往左边找就是5这个结点进行调整,将5和9置换,然后7和5置换(因为旧的结点数据被更替,需要重新调整结点数据)。如下二图:

    
也可以这样理解,找5下面的“最大链”然后将5与最小的替换  “最大链”即:5下面谁最大,9然后9下面谁最大7。最大链就是5,9,7
然后将5暂时移除,然后将大于5部分的链段上移一格,5填补空位。即9和7均上移一格,5插入到7的位置。于是变成了9,7,5

然后处理下一个结点3,按照上述方法则最终的堆为:

对应的数组是[9,8,6,7,3,2,1,5,4]

然而,这第一步构建堆的代码怎么写呢?

问题3.如何找最后一个非叶子结点,以及如何往上找非叶子节点呢?

我自己研究了下,发现,叶子结点和非叶子结点将近是各占一般,最多叶子结点多一个。

为什么是这样的我的探究证明参考https://blog.csdn.net/qq_26440559/article/details/88778331

因此最后一个非叶子结点是在数组的中间位置,那么具体在哪里,如果数组长度为偶数,那么叶子结点和非叶子结点均占一半,最后一个非叶子结点在中间两个结点的前面哪一个,比如说,一个数组arr,长度为8,那么最后一个非叶子结点在第4位,数组索引为3(数组索引是从0开始的)

如果数组长度为奇数,那么最中间的那个结点就是叶子结点,往前一个结点就是最后一个非叶子结点,比如一个数组arr长度为9,那么最后一个非叶子结点在第4位,数组索引位3

如果arr长度位N,那么最后一个非叶子结点位置在 N/2的位置(如果N位奇数怎么办,在java中,7/2=3,因为数字默认是int类型,返回值也会转为int类型,会直接舍弃掉小数位)那么数组索引位置在N/2-1的位置

如何往上找非叶子结点,只要当前索引-1就是上一个非叶子结点。因为非叶子结点和叶子结点分别是分布在数组的前面一部分和后面一部分

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值