堆排序

        //xk> 什么是堆?

        堆的结构满足两点性质:1. 堆是一棵完全二叉树。2. 任意节点的值小于它的所有后代。

        性质2又称为堆序(heap order)性。节点值小于后代的堆即为小根堆,显然其根节点的值是最小的。相对地有大根堆。小根堆常用于XXX,大根堆常用于XXX。

        不要求堆是满二叉树。也不要求其节点左孩子的值小于右孩子,堆不必要是二叉排序树(又叫二叉查找树)。


        //xk> 堆的存储

        堆是一种特殊的完全二叉树,用指针实现树状结构是很直观的想法。

        但完全二叉树是个例外,因为它很有规律,可以用一个数组表示而不需要指针。对于数组位置i上(i从1开始,数组位置0留作它用,哨兵)的元素,其左儿子在位置2i上,而右儿子在位置(2i + 1),它的父亲则在(i / 2)向下取整的位置上。数组实现的一个限制是需要预先设定堆的大小。


        //xk> 堆节点插入

        形象上理解,堆节点的插入就是将节点插入到完全二叉树的最后一个位置(满足性质1),然后不断跟其父节点的值进行比较,如果新节点的值比父节点小,就“交换”它们的位置,新节点上浮(英语术语用percolate up,译作上滤)到上一层,如此循环直至新节点的值小于其父节点(满足性质2)。

        没有完全二叉树的重新平衡、旋转操作等等,就是新节点在树结构中上冒到合适位置,so easy.

        实现上,交换操作的开销是不必要的,我们可以先插入一个空穴作为新节点的插入位置,然后将空穴上滤到完全二叉树中适当位置后,再将新节点填入到空穴中。空穴只是一个位置标记,其原有的值可以被覆盖掉,而不必进行交换操作。

        循环的终止条件要留心,如果新节点是新的最小值,它将一直上浮到根。我们可以明确地在循环终止条件中判断 1 == i。另一种常用的方法是在0 == i的哨兵位置放一个比所有堆节点的可能值都小的值作为sentinel,这样可以避免每次循环都多做一次测试。用空间复杂度换时间复杂度,反正数组位置0要空着不便于直接用于完全二叉树。

        插入操作的时间复杂度最坏为O(logN).


        //xk> 删除最小元(小根堆)

        删除最小元以类似插入的方式处理。删除一个最小元时,在根节点处产生了一个空穴。由于现在堆少了一个元素,因此堆中最后一个元素X必须移动到该堆的某个地方。如果X可以被放入空穴中,那么操作完成。这一般不会出现,因此将空穴的两个儿子中的较小者放入空穴中,这样就把空穴向下推了一层。如此循环直到X可以被放入空穴中。

        空穴的下移路径可能并不奔着最后一个元素X,没关系,只要能把X放入空穴就行。我们的做法是将X置入沿着根开始包含最小儿子的一条路径上的一个正确的位置。这种一般的策略叫做下滤(percolate down). 同样使用空穴的概念来避免多余的交换操作。

        在堆的实现中容易出错的一个地方是,当堆中有偶数个元素时,某个节点只有一个子节点,在编程时要小心节点并不总有两个儿子。

        删除最小元的最坏运行时间和平均运行时间都为O(logN).


        //xk> 创建堆

        最直观的思路:对给定的N个无序值,做N次堆的插入操作。最坏时间复杂度为O(N * logN).


        //xk> 堆排序

        首先将N个元素建立为堆,最坏时间复杂度是O(N * logN). 然后执行N次输出并删除最小元操作,最坏时间复杂度也是O(N * logN). 因此堆排序的最坏时间复杂度也是O(N * logN).

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值