1. 堆的应用之求topk
topk问题,无论是静态数据集合还是动态数据集合,都可以使用小顶堆的方式解决。如果待处理的数据是动态数据集合,小顶堆的使用优势非常明显,只需要维持一个k大小的小顶堆,就可以很轻松的实现获取动态topk,因为加入新的数据进去的时候可以快速的实现维护这个小根堆。本文仅实现了对静态数据集合的topk元素的获取:
- 时间复杂度是o(nlogk)
- 空间复杂度为o(1)
class Solution{
public int[] findTopK(int[] a,int k){
PriorityQueue<Integer> pq = new PriorityQueue<>(k); //PriorityQueue的底层实现就是堆
for(int i=0; i<a.length; i++){
if(pq.size()<k){
pq.add(a[i]); //构建原始的小顶堆
}else{
int value = pq.peek(); //将堆顶元素取出
if(a[i] > value){ //在大于堆顶元素的情况下,将堆顶元素删除,a[i]入堆
pq.poll();
pq.add(a[i]);
}
}
}
int[] result = new int[k];
int count = 0;
while(pq.size() != 0){ //得到最终的前k个元素
result[count++] = pq.poll();
}
};
}
2. 堆的应用之求中位数
求一个数组的中位数需要分情况讨论,如果数组的长度n是奇数,则该数组的中位数是第n/2+1个数,如果该数组的长度是偶数,则该数组的中位数有两个,第n/2个数和第n/2+1个数,我们可以选第n/2个数作为其中位数。
具体算法思想为:为了求动态数据集合的中位数,我们可以用一个大顶堆存储数据集合的前n/2个(或者n/2+1个元素),用另一个小顶堆存储数据集合的后n/2个元素。有新的元素输入时,若新的元素小于大顶堆顶部元素则将新的元素加入大顶堆,若新的元素大于大顶堆元素,则将元素加入小顶堆中。在元素加入两个堆中任意一个过后,为了保证大顶堆的堆顶元素为中位数,我们需要根据元素的总个数的奇偶性调整大顶堆和小顶堆的元素数量(一个堆向另一个堆转移元素)。
- 算法的时间复杂度为o(nlogn)
- 算法的空间复杂度为o(n)
class Solution{
public int count = 0; //计数元素的个数
public PriorityQueue<Integer> bigRootHeap = new PriorityQueue<>(51, new Comparator<Integer>{
@override
public int compare(Integer o1, Integer o2){ //需要覆盖比较器,实现大顶堆
return o2.compareTo(o1);
}
});
public PriorityQueue<Integer> littleRootHeap = new PriorityQueue<>(50);
public void putNum(int value){ //输入数据
count++;
//初始状态,两个堆都为空
if(bigRootHeap.isEmpty()&&littleRootHeap.isEmpty()){
bigRootHeap.add(value);
return;
}
if(bigRootHeap.peek()>value){
bigRootHeap.add(value);
}else{
littleRootHeap.add(value);
}
int halfCount = count/2;
if(count%2 == 0) { //元素个数为偶数
if(bigRootHeap.size() == halfCount)
return;
else if(bigRootHeap.size() > halfCount){
move(bigRootHeap, littleRootHeap, bigRootHeap.size()-halfCount);
return;
}else{
move(littleRootHeap, bigRootHeap, littlteRootHeap.size()-halfCount);
}
}else{ //元素个数为奇数
if(bigRootHeap.size() < halfCount+1){
move(littleRootHeap, bigRootHeap, littlteRootHeap.size()-halfCount);
}else{
movemove(bigRootHeap, littleRootHeap, bigRootHeap.size()-halfCount);
}
}
};
//实现一个堆向另一个堆中转移元素
public void move(PriorityQueue<Integer> from, PriorityQueue<Integer> to, int n){
for(int i=0; i<n; i++)
to.add(from.poll());
};
//大顶堆的堆顶元素即为中位数
public int getMidValue(){
return bigRootHeap.peek();
}
}