数据结构与算法---堆

需求

假如我们现在要设计一种数据结构,用来存放整数,要提供三个接口:
添加元素、获取最大值、删除最大值

我们可以使用动态数组或者双向链表来做。
获取最大值:需要遍历整个数组或者链表,因此,时间复杂度为O(n)
删除最大值:需要遍历整个数组或者链表,因此,时间复杂度为O(n)
添加元素:默认是添加到最后。
动态数组添加在最后,在知道数组size的情况下,添加元素的时间复杂度为O(1)
双向链表添加在最后,由于双向链表有first和last指针,因此,可以直接找到最后的元素,所以,添加元素的时间复杂度为O(1)

当然,我们也可以使用有序的动态数组或者双向链表来做
获取最大值:如果从小到大排序的话,因为是有序的,所以直接取出最后一个元素即可。因此,时间复杂度为O(1)
删除最大值:如果从小到大排序的话,因为是有序的,所以直接取出最后一个元素即可。因此,时间复杂度为O(1)
添加元素:默认是添加到最后。
动态数组添加在最后,在知道数组size的情况下,插入元素的时间复杂度为O(1),但是插入完毕后需要做比较,保证数组还是有序的,这就要做比较,因此,这样其时间复杂度为O(n)
双向链表添加在最后,由于双向链表有first和last指针,因此,可以直接找到最后的元素,并插入。但是插入完毕后需要做比较,保证数组还是有序的,这就要做比较,因此,这样其时间复杂度为O(n)
但是,我们只是要做获取最大值,删除最大值。就用到了全排序,维护全排序有点浪费。

当然,我们还可以使用平衡二叉搜索树BBST来做
由于BBST的最大值一定是位于最右边,因此:
获取最大值:查找最右边的元素,树的高度为logn,因此,时间复杂度为O(logn)
删除最大值:查找最右边的元素,树的高度为logn,因此,时间复杂度为O(logn)
添加元素:AVL树和红黑树的添加元素都是O(logn)
但,维护一个AVL树或者红黑树也是够复杂的。

因此,有没有更好的数据结构可以解决我们的需求呢?
这就是我们要讲的

在这里插入图片描述


堆(Heap)

堆的获取最大值时间复杂度为O(1)
删除最大值时间复杂度为O(logn)
添加元素的时间复杂度为O(logn)

并且,堆广泛应用在Top K的问题中

什么是Top K问题?

在海量数据里面,找出前K个数据。
比如:在100万整数中,找出最大的100个数。

当然,堆是解决Top K的一种方法之一,而不是解决Top K的唯一方法。

堆是一种树状的数据结构(注意,不要跟内存存储的堆栈空间混淆)
常见的堆实现有:
二叉堆(完全二叉堆)
多叉堆
索引堆
二项堆
等等

堆的一个重要性质:任意节点的值总是>=(<=)子节点
如果任意节点的值总是>=子节点的堆,称为:最大堆、大根堆、大顶堆
如果任意节点的值总是<=子节点的堆,称为:最小堆、小根堆、小顶堆

既然要有>=或者<=,可见堆需要具备可比较性。


基本上涉及到的接口有:

int size(); // 元素的数量
boolean isEmpty(); // 是否为空
void clear(); // 清空
void add(E element); // 添加元素
E get(); // 获得堆顶元素
E remove(); // 删除堆顶元素
E replace(E element); // 删除堆顶元素的同时插入一个新元素

二叉堆(Binary Heap)

二叉堆的逻辑结构就是一棵完全二叉树,所以也叫完全二叉堆

对于完全二叉树的一些性质,二叉堆的底层(物理结构)一般用数组实现。

在这里插入图片描述

最大堆的添加
在这里插入图片描述

最大堆的删除
在这里插入图片描述

批量建堆

在这里插入图片描述
在这里插入图片描述


Top K问题

从n个整数中,找出最大的前k个数(k远远小于n)

如果使用排序算法进行全排序(1,2,3…),需要O(nlogn)的时间复杂度

如果使用二叉堆来解决,可以达到O(nlogk)的时间复杂度

过程:
新建一个小顶堆
扫描n个整数
先将遍历到的前k个数放入堆中
从k+1个数开始,如果大于堆顶元素,就使用replace操作(删除堆顶元素,将第k+1个元素添加到堆中)
扫描完毕后,堆中剩下的元素,就是数据里面的最大的k个元素

同样,如果是查找一大堆数据里面的前k个最小元素
就使用大顶堆
如果小于堆顶元素,则使用replace操作


堆的实用—优先级队列

优先级队列(priority queue)
普通队列都是FIFO

优先级队列是都可以进,但每次取优先级最高的元素出队。
这不正好是堆的特性,因此,可以利用最大堆或者最小堆来实现优先级队列。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值