批量建堆
对堆的介绍请参考数据结构之堆。
批量建堆(Heapify):就是将已经存在n个元素的数组批量添加至堆中,而不是遍历数组一个一个将元素添加至堆中。
遍历数组一个一个添加元素至堆中,时间复杂度为O(nlogn),而使用批量建堆,时间复杂度最低可以降为O(n)。
批量建堆有2种实现方法:
- 自上而下的上滤
- 自下而上的下滤
自上而下的上滤
自上而下的上滤类似于从第2个元素开始依次添加。
代码实现如下:
for (int i = 1; i < size; i++) {
siftUp(i);
}
自下而上的下滤
自下而上的下滤类似于从最后一个非叶子节点开始往前删除。
代码实现如下:
for(int i = (size >> 1) - 1; i >= 0; i--) {
siftDown(i);
}
效率对比
自上而下的上滤的时间复杂度为所有节点的深度之和,n个节点的深度都是O(logn),所以时间复杂度为O(nlogn)。
自下而上的下滤的时间复杂度为所有节点的高度之和,时间复杂度为O(n)。
下面是对自下而上的下滤的时间复杂度为O(n)的论证:
假设是一颗满二叉树,节点总个数为n,树高为h,那么有n=2^h−1
设所有节点的树高之和为H(n),那么有H(n)=2^0∗(h−0)+2^1∗(h−1)+2^2∗(h−2)+⋯+2^(h−1)∗(h−(h−1))
H(n)= h*(2^0+2^1+2^2+ ⋯+2^(h −1)-(1∗2^1+2∗2^2+3∗2^3+ ⋯+(h−1)∗2^(h−1))
H(n)= h∗2^(h−1)−(h−2)∗2^h+2)
H(n)= h∗2^h−h−h∗2^h+2^(h+1)−2
H(n)= 2^(h+1)−h−2
H(n)= 2∗(2^h−1)−h
H(n)= 2n−h
H(n)= 2n−log2(n+1) = O(n)
S(h) = 1∗2^1+2∗2^2+3∗2^3+ ⋯+(h−1)∗2^(h−1)
2S(h) = 2*(1∗2^1+2∗2^2+3∗2^3+ ⋯+(h−1)∗2^(h−1))= 2∗2^2+3∗2^3+ ⋯+(h−1)∗2^h
S(h)=2S(h)-S(h)=(h−2)∗2^h+2
疑惑
以下方法可以批量建堆么?
- 自上而下的下滤
- 自下而上的上滤
上述方法不可行,为什么?
- 自上而下的上滤的本质为添加
- 自下而上的下滤的本质为删除
批量建堆的代码实现如下:
public BinaryHeap(E[] data, Comparator<E> comparator) {
this.comparator = comparator;
if(null == data || 0 == data.length) {
this.elements = (E[]) new Object[DEFAULT_CAPACITY];
} else {
// 批量建堆
int length = Integer.max(data.length, DEFAULT_CAPACITY);
size = data.length;
this.elements = (E[]) new Object[length];
// 将元素从data复制到elements
for (int i = 0; i < size; i++) {
this.elements[i] = data[i];
}
heapify();
}
}
private void heapify() {
// 自下而上的下滤,类似删除
// 时间复杂度O(n)
// (size >> 1) - 1 最后一个非叶子节点的索引
for(int i = (size >> 1) - 1; i >= 0; i--) {
siftDown(i);
}
}
Top K问题
从n个整数中,找出最大的前k个数(k远远小于n)
- 如果使用排序算法进行全排序,需要O(nlogn)的时间复杂度(快速排序)
- 如果使用二叉堆来解决,可以使用O(nlogk)的时间复杂度来解决
查找步骤:
- 新建一个小顶堆
- 扫描n个整数,先将遍历到的前k个数放入堆中,从第k+1个数开始,如果大于堆顶元素,就使用replace操作(删除堆顶元素,将第k+1个数添加到堆中)
- 扫描完毕后,堆中剩下的就是最大的前k个数
如果是找出最小的前k个数呢?用大顶堆,如果小于堆顶元素,就使用replace操作。
查找最大的前三个数的代码实现如下:
public void testTopK() {
int[] array = {16, 4, 27, 32, 35, 56, 79, 88, 90};
int k = 3;
BinaryHeap<Integer> binaryHeap = new BinaryHeap<>((o1, o2) -> o2 - o1);
for (int i = 0; i < array.length; i++) {
if(i < k) {
binaryHeap.add(array[i]);
} else {
if(array[i] > binaryHeap.get()) {
binaryHeap.replace(array[i]);
}
}
}
while (!binaryHeap.isEmpty()){
System.out.println(binaryHeap.remove());
}
}
更多精彩内容关注本人公众号:架构师升级之路