优先级队列(堆)

堆分为大根堆和小根堆

其性质:

        堆中某个节点的值总是不大于或者不小于其父节点的值;

        堆总是一颗完全二叉树 

堆采用顺序的方式存储

如何创建一个堆?采用向下调整的方式

已知最后一个结点(孩子结点)的下标为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;
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值