LeetCode基础-排序-二叉堆排序(优先队列)

有些情况下要求不一定要求数组全部有序,或者说不一定要一定性全部有序。
有些情况下先收集一些元素,找到这些元素中的最大值,然后重复这个步骤。
比如,电脑或手机上运行的多个应用程序,可以给每个程序分配一个优先队列,并总是处理下一个优先级最高的那个应用程序。

这就需要优先队列了,它支持这两种操作:

  • 删除最大元素
  • 插入元素

优先队列与队列(删除最老元素)和栈(删除最新元素)类似,但更高效。

二叉堆数据结构能够很好的实现优先队列的两种操作。二叉堆就像是一个完全二叉树。
有序的堆能够保证父结点大于两个子结点,根结点是最大结点。
这里写图片描述

位置 k 的结点的父结点的位置是 k/2,而他的子结点的位置是 2k 和 2k+1。

我们用长度为 N+1 的数据 pq[] 来表示一个大小为 N 的堆,不使用 pq[0](为了方便计算),所有堆元素放在 pq[1] 至 pq[N] 中。

首先将二叉堆排序:

  • 从下至上的有序化(上浮)swim()
  • 从上至下的有序化(下沉)sink()

如果堆的有序状态因为某个结点变得比它的父结点更大,那么就需要交换二者的顺序,以重新恢复堆的规则,直至小于其父结点。
这里写图片描述

public void swim(int k)
{
    while(k > 1 && k/2  < k)
    {
        exchange(k/2, k);
        k = k/2;
    }
}

如果堆的有序状态因为某个结点变得比它的两个子结点或是其中之一更小,那么就需要与两个子结点的较大者交换顺序,以重新恢复堆的规则,直至大于其子结点。
这里写图片描述

public void sink(int k)
{
    while(2*k < = N)
    {
        int j = 2*k;
        if(j < N && j < j+1)
        {
            j++;
        }
        if(k >= j)
        {
            break;
        }
        exchange(k, j);
        k = j;
    }
}

当插入元素时,我们将新元素加至数组末尾,改变堆的大小,然后让这个元素 swim 到合适的位置。
此操作的比较次数不超过 lgN+1

当删除最大元素时,我们从数组顶端删去最大元素,改变堆的大小,并将数组的最后一个元素放至顶端,然后让这个元素 sink 至合适的位置。
此操作的比较次数不超过 2 * lgN

这里写图片描述

任意优先队列可以变成堆排序。堆排序可以分为两个阶段:

  • 将数组构造成堆:从右至左用 sink() 函数构造子堆。
  • 下沉排序,从堆中按递减顺序取出所有元素并排序。
public void sort(int[] a)
{
    int N = a.Length;
    for(int k = N/2; k >= 1; k--)
    {
        sink(a[k]);
    }
    while(N > 1)
    {
        exchange(a[1], a[N--]);
        sink(a[1]);
    }   
}

上面的代码将 a[1] 到 a[N] 的元素进行排序,for 循环构造堆。while 循环把 a[1] 和 a[N] 交换并修复堆,直到堆变空。

这里写图片描述

常见题型:

  • 从10亿个元素中找出最大的10个。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值