Java——堆笔记+topk问题+堆排序

堆的概念

  1.  堆逻辑上是一棵完全二叉树
  2.  堆物理上是保存在数组中;
  3.  满足任意结点的值都大于其子树中结点的值,叫做大堆,或者大根堆,或者最大堆;
  4.  反之,则是小堆,或者小根堆,或者最小堆;
  5. 堆的作用是快速找集合中的最值

下标关系

已知双亲 (parent) 的下标,则:
左孩子 (left) 下标 = 2 * parent + 1;
右孩子 (right) 下标 = 2 * parent + 2;
已知孩子(不区分左右) (child) 下标,则:
双亲(parent)下标 = (child - 1) / 2 ;

初始条件

    public int[] elem;
    public int usedSize;
    public TestHeap(){
        this.elem=new int[10];
    }

向下调整函数shiftDown

以转换为大堆为例

/**
 *向下调整函数的实现
 * parent:每个树的根节点
 * len:每棵树的调整结束位置 10
 * **/

向下调整就是每次交换完之后会沿着当前这个路径向下检测

1.从最后一个子树出发

2.每颗子树都是向下调整的

如何调整为大根堆 

 问题:

1.如何找到最后一颗子树?

父亲节点:(child-1)/2;

此时节点:((len-1)-1)/2

2.parent--就可以遍历完每一棵子树了

3.最主要的问题就是写一个函数进行向下调整

4.每棵树的调整结束位置,如何判定?

每棵树调整的结束位置实际都是一样的len。

    //此时的时间复杂度:每层的节点个数*每层向下检测的层数O(n)
    public void shiftDown(int parent,int len){
        int child=2*parent+1;
        //最起码是有左孩子的,至少有一个孩子
        while(child<len){
            if(child<len&&elem[child]<elem[child+1]){//如果只有左孩子,那左孩子就是较大值,不用再++,否则空指针异常
                //child是用来保证较大孩子的下标
                child++;
            }
            if(elem[child]>elem[parent]){
                int temp=elem[child];
                elem[child]=elem[parent];
                elem[parent]=temp;
                //交换完之后需要继续向下检测
                parent=child;
                child=2*parent+1;
            }else if(elem[child]<elem[parent]){
                break;
            }
        }
    }

createHeap函数

    public void createHeap(int [] array){
        for (int i = 0; i < array.length; i++) {
            elem[i]=array[i];
            usedSize++;
        }
        for (int parent = (usedSize-1-1)/2; parent >= 0; parent--) {
            shiftDown(parent,usedSize);
        }
    }

PriorityQueue中常用方法模拟实现

offer();

isFull()

    public boolean isFull(){
        return usedSize==elem.length;
    }

shifUp()向上调整

    public void shifUp(int child){
        int parent=(child-1)/2;
        while(child>0){
            if(elem[child]>elem[parent]){
                int temp=elem[child];
                elem[child]=elem[parent];
                elem[parent]=temp;
                child=parent;
                parent=(child-1)/2;
            }else {
                break;
            }
            parent=(child-1)/2;
        }
    }

offer()

    //向上调整
    public void offer(int val){
        if(isFull()){
            //扩容
            elem= Arrays.copyOf(elem,2*elem.length);


        }
        elem[usedSize++]=val;
        //注意:
        shifUp(usedSize-1);

    }

poll();

/****
 * 关于出队列:每次保证出的是最大或者最小的元素
 * 交换0下标和最后一个下标的元素
 * 调整0下标这棵树
 * **/

isEmpty()

   public boolean isEmpty(){
        return usedSize==0;
    }

poll()

    public int poll(){
        if(isEmpty()){
            throw new RuntimeException("优先级队列为空");
        }
        int tmp=elem[0];
        elem[0]=elem[usedSize-1];
        elem[usedSize-1]=tmp;
        usedSize--;
        shiftDown(0,usedSize);
        return tmp;

    }

peek();

 /**
     * 返回队首元素
     * **/
    public int peek(){
        if(isEmpty()){
            throw new RuntimeException("优先队列为空");
        }
        return elem[0];
    }

堆排序

 /**
     * 堆排序
     * 从小到大排序
     * **/
    /***
     * 从小到大排序:
     * 1.调整为大根堆
     * 2.0下标与最后一个未排序的元素进行交换
     * 3.end--
     * */
    public void heapSort(){
        int end=usedSize-1;
        while(end>0){
            int temp=elem[0];
            elem[0]=elem[end];
            elem[end]=temp;
            shiftDown(0,end);
            end--;
        }
    }

topk问题

/**
 * 求数组中前k个最大元素 间一个小堆,大小是k,堆顶元素就是第k大的元素;
 * 求数组中前k个最小的元素 建立一个大堆,大小是k,堆顶元素就是第k小的;
  
 * */
    //这里求前k个最小的元素:
    public static int[] topK(int[] array,int k){
        //创建一个大小为K的大根堆
        //java中对象的比较
        PriorityQueue<Integer> maxHeap=new PriorityQueue<>(k, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2-o1;
            }
        });
        //2.遍历数组当中的元素,前k个元素放在队列当中,
        for (int i = 0; i < array.length; i++) {
                if(maxHeap.size()<k){
                    maxHeap.offer(array[i]);
                }else{
                    //3.从k+1个元素开始每个元素需要和堆顶元素进行比较
                    int top=maxHeap.peek();
                    if(top>array[i]){
                        //先弹出
                        maxHeap.poll();
                        //后存入
                        maxHeap.offer(array[i]);
                    }
                }
            }
        int[] tmp=new int[k];
        for (int i = 0; i < k; i++) {
            tmp[i]=maxHeap.poll();
        }
        return tmp;
    }

 

 

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sqyaa.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值