堆
无论是在算法中,还是在系统设计的场景中,堆,看起来对于寻找最大(小)k 个数很适用。
比如在leetcode 347.前 K 个高频元素 中,需要寻找一个数组中前 k 个高频的数字。
比如在一些场景设计题目中,他会说我现在有 10 万条数据,我需要找到前 100 个最大的数。
一般来说,需要得到前 k 个元素的,都会推荐使用堆结构来处理。
算法题目中
在 leetcode 的题目中,由于他没有限制堆的大小。因此不论是使用最大堆还是最小堆,我们都能得到前 K 高频数字
系统设计题目中
在场景题目中,这里的堆就是有界队列。此时我们就需要在意应该用最大堆还是最小堆。比如我们要在 10 万个数字中找到前100 个最大的数字。那么就应该使用最小堆。
为什么呢?
最小堆,结构是各个子树的父节点总是小于子节点,因此树根节点最小。当插入一个新节点时,只需要与根节点进行比较,就知道是否应该将节点插入堆中。如果要插入的节点比根节点小,那就不能插入堆中(根节点已经是最小的,要插入的节点比根节点还小,那么肯定就不是前 k 个最大的数了)。如果要插入的节点比根节点大,那么就可以插入到堆中一个合适的位置,并且移除堆中的根节点。
Java 中堆的实现
PriorityQueue: An unbounded priority queue based on a priority heap.
Java 中的PriorityQueue 是无界的。想让他有界,可以在 add 时判断大小,如果超出你设定的大小,则弹出一个元素。
que.add(d);
if (que.size() > YOUR_LIMIT)
que.poll();
// 这是一个小顶堆
PriorityQueue<Integer> queue = new PriorityQueue<>(5, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1-o2;
}
});
// 这是一个大顶堆
PriorityQueue<Integer> queue = new PriorityQueue<>(5, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2-o1;
}
});
通过 compare 来决定元素顺序,默认是从小到大排列,也就是小顶堆。