堆分为大根堆和小根堆
其性质:
堆中某个节点的值总是不大于或者不小于其父节点的值;
堆总是一颗完全二叉树
堆采用顺序的方式存储
如何创建一个堆?采用向下调整的方式
已知最后一个结点(孩子结点)的下标为len-1;其父亲结点下标为(孩子结点-1)/2,也就是(len-2)/2
public int[] elem;
public int usedSize;
public TestHeap(){
this.elem=new int[10];
this.usedSize=0;
}
public void initArray(int[] array){
elem= Arrays.copyOf(array,array.length);
usedSize=elem.length;
}
//建堆(大堆根)
public void createHeap(){
for(int parent=(usedSize-1-1)/2;parent>=0;parent--){
shiftDown(parent,usedSize);
}
}
private void shiftDown(int parent,int len){
int child=2*parent+1;
while (child<len){
if (child+1<len && elem[child]<elem[child+1]){
child++;
}
if (elem[child]>elem[parent]){
swap(elem,child,parent);
parent=child;
child=2*parent+1;
}else {
break;
}
}
}
public void swap(int[] array,int i,int j){
int tmp=array[i];
array[i]=array[j];
array[j]=tmp;
}
创建出了一个堆,那么如何插入元素和删除元素呢
插入元素要采取向上调整的方法
public void offer(int x){
if (isFull()){
elem=Arrays.copyOf(elem,2*elem.length);
}
this.elem[usedSize]=x;
usedSize++;
shiftUp(usedSize-1);
}
private void shiftUp(int child){
int parent=(child-1)/2;
while (child>0){
if (elem[child]>elem[parent]){
swap(elem,child,parent);
child=parent;
parent=(child-1)/2;
}else {
break;
}
}
}
public boolean isFull(){
return usedSize==elem.length;
}
删除元素时,只能删除堆顶元素
//出堆
public int poll(){
if (isEmpty()){
return -1;
}
int old=elem[0];
swap(elem,0,usedSize-1);
usedSize--;
shiftDown(0,usedSize);
return old;
}
public boolean isEmpty(){
return usedSize==0;
}
n个结点的堆,高度d = logn。根为第0层,则第i层结点个数为2^i,考虑一个元素在堆中向下移动的距离,这种算法时间代价为O(n)。由于堆有log n层深,插入结点、删除普通元素和删除最小元素的平均时间代价和时间复杂度都是O(log n)。
例题:求最小或最大k个数问题
首先建堆,如果是求最小k个数问题,建大堆;反之求最大k个数问题,建小堆
其次,将剩余的元素依次和建好的堆进行比较:
最小k个数:如果剩余元素比堆的top小,则替换之前的top元素,依次完成剩余的比较;
最大k个数:如果剩余元素比堆的top大,则替换之前的top元素,依次完成剩余的比较;
class Solution {
public int[] smallestK(int[] arr, int k) {
int[] ret=new int[k];
if(k==0){
return ret;
}
PriorityQueue<Integer> maxPQ=new PriorityQueue<>(k,new Comparator<Integer>(){
@Override
public int compare(Integer o1,Integer o2){
return o2-o1;
}
});
for(int i=0;i<arr.length;i++){
if(maxPQ.size()<k){
maxPQ.offer(arr[i]);
}else{
//获取到堆顶元素
int top=maxPQ.peek();
//找到k个最小的
if(arr[i]<top){
maxPQ.poll();
maxPQ.offer(arr[i]);
}
}
}
for(int i=0;i<k;i++){
ret[i]=maxPQ.poll();
}
return ret;
}
}