3_二叉堆与堆排序

  • 首先现在有这么个需求:一堆元素依次加入一个队列,但是它们有权值,每次都让权值小的元素先出队。笨办法就是每次都顺序扫描所有元素,聪明的办法就是让队列在加入和离开元素时总保持有序;

  • 二叉堆就可以解决上面的问题,它的形状和二叉树一样,入队和出队操作都是O(logN)的,而且一个数组就搞定,不用像二叉树一样把每个元素的左右儿子都链接住,因为二叉堆是一棵__完全二叉树__

  • 接下来的说明都以小顶堆为例,即最小的元素为根节点,排在最前面

  • 小顶堆的定义就是对于每个结点来说,它都小于它的左右子结点(如果有的话);并且对于从每个结点开始的子堆也都是小顶堆

  • 如果小顶堆是从零开始慢慢建立的话,那很好办:

    每次都先把新来的元素放在下一个空位上(为了满足完全二叉树的条件),然后如果新来的元素比父亲元素大,放在这就结束了;如果新来的元素比父亲元素小,那就交换它和它父亲,再次向上比较,直到满足它的父亲比它小这一条件为止;

    又由于二叉堆是完全二叉树,所以一个父节点的两个子节点的序号一定是2i+1和2i+2(从0开始编号根节点),所以很容易确定当前结点的父亲或者儿子的序号

  • 出队的操作也很简单:

    一种可能的操作是:

    把根元素移出去,然后根元素的两个儿子同台竞技,选个更小的填坑,然后就出现了新的坑,再继续递归的填坑就行了

    但是这样__不行!!__因为很可能经过各种填坑之后,最底层最左边的结点升上去了,然后完全二叉树的条件不满足了

    正确的操作是

    把根元素移出去,然后钦定最后一个元素为新的根元素,然后这个新的根元素自然是德不配位,它慢慢向下调整下放到它应该在的位置,这样就可以保证有序且是完全二叉树

  • 有一种情况是,现在队伍已经有了,虽然是杂乱无章的,但是不能像从零开始建立队伍的方式建立小顶堆了,这可如何是好呢?

    刚才我们已经有了充分的经验:套路就是递归的完成这个操作:保证左子堆是小顶堆,再保证右子堆是小顶堆,然后再把当前结点下放到它应该在的位置

    但是也可以不用递归,类似于动态规划的方式,先从最末尾开始变成小顶堆,都排好之后再动上层的元素,道理是相同的

  • 最后说一下__堆排序__,其实它是上一个问题的加强版:队伍已经有了,是杂乱的,现在要把它们整理好

    如果像刚才一样,建立好小顶堆就行了吗?

    不是的,因为我们把它拉伸成一棵树它是有序了,但作为一个数组来看,它还是无序的;

    所以,建立成小顶堆只是第一步;

    然后还是一次出队,只不过出队的元素不要扔,而是每次都从数组的末尾开始依次放置,再进行一波出队的操作(钦定最后一个元素是新的根元素,再下放到应该在的位置,确定合适的接班根节点)。但是要注意,这次下放是有范围的,例如最小的那个元素本来是根元素,现在它放在了数组的最后,那么再次调整二叉堆的时候范围就不应该包括数组的最后一个元素了(否则本来要退休的这个最小的元素不就又变成根节点了么),所以跟之前出队操作的区别就是__现在的调整要有范围__

    所以,如果想让排成升序,那么就建立大顶堆;如果降序那就建立小顶堆

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值