堆(Heap or PriorityQueue) Java

举报
王小贰 发表于 2021/01/31 12:29:08 2021/01/31
【摘要】 认识堆(优先级队列)1.堆又叫优先级队列,逻辑上上是一棵完全二叉树,堆物理上基于数组实现2.堆可分为大堆(大根堆、最大堆)和小堆(小根堆、最小堆)堆(优先级队列)操作方法public class MyHeap { private int[] elem; private int usedSzie; public MyHeap(int k){ this.elem ...

认识堆(优先级队列)

1.堆又叫优先级队列,逻辑上上是一棵完全二叉树,堆物理上基于数组实现
2.堆可分为大堆(大根堆、最大堆)和小堆(小根堆、最小堆)

在这里插入图片描述

堆(优先级队列)操作方法

public class MyHeap {

    private int[] elem;
    private int usedSzie;

    public MyHeap(int k){
        this.elem = new int[k];
    }

    //创建大堆
    public void createBigHeap(int[] arr){
        for (int i = 0; i < arr.length; i++) {
            this.elem[i] = arr[i];
            this.usedSzie++;
        }
        //i表示父亲结点,依次调整
        for (int i = (this.usedSzie-2)/2; i >= 0 ; i--) {
            adjustDown(i, this.usedSzie);
        }
    }

    //向下调整
    public void adjustDown(int parent, int len){
        int child = 2*parent+1;
        //len代表数组实际长度
        //child小于len说明 parent有左孩子,不能确定是否有右孩子 child==len说明 parent没有左右孩子
        while(child < len){
            //child+1 < len保证当前父亲结点有右孩子
            //当左孩子小于右孩子时,child走到右孩子下标,保证child是左右孩子最大值下标
            if(child+1 < len &&this.elem[child] < this.elem[child+1]){
                child++;
            }
            //当孩子结点大于父亲结点时,交换
            if(this.elem[child] > this.elem[parent]){
                int tmp = this.elem[child];
                this.elem[child] = this.elem[parent];
                this.elem[parent] = tmp;
                parent = child;
                child = 2*parent+1;
            }else{
                //我们是从最后一棵树调整的
                //当this.elem[child] <= this.elem[parent]时,表明此时的树是大根堆,后面不需要循环了
                break;
            }
        }
    }

    //创建小堆
    public void createSmallHeap(int[] arr){
        for (int i = 0; i < arr.length; i++) {
            this.elem[i] = arr[i];
            this.usedSzie++;
        }
        //i表示父亲结点,依次调整
        for (int i = (this.usedSzie-2)/2; i >= 0 ; i--) {
            adjustDown1(i, this.usedSzie);
        }
    }

    //向下调整
    public void adjustDown1(int parent, int len){
        int child = 2*parent+1;
        //len代表数组实际长度
        //child小于len说明 parent有左孩子,不能确定是否有右孩子 child==len说明 parent没有左右孩子
        while(child < len){
            //child+1 < len保证当前父亲结点有右孩子
            //当左孩子小于右孩子时,child走到右孩子下标,保证child是左右孩子最大值下标
            if(child+1 < len &&this.elem[child] > this.elem[child+1]){
                child++;
            }
            //当孩子结点大于父亲结点时,交换
            if(this.elem[child] < this.elem[parent]){
                int tmp = this.elem[child];
                this.elem[child] = this.elem[parent];
                this.elem[parent] = tmp;
                parent = child;
                child = 2*parent+1;
            }else{
                //我们是从最后一棵树调整的
                //当this.elem[?child] <= this.elem[parent]时,表明此时的树是大根堆,后面不需要循环了
                break;
            }
        }
    }


    //向上调整
    //这里的child第一次就是入队元素的下标,
    public void adjustUp(int child){
        int parent = (child-1)/2;
        //child等于0相当于已经调整了位置,此时parent为负数了
        while(child > 0){
            if(this.elem[child] > this.elem[parent]){
                int tmp = this.elem[child];
                this.elem[child] = this.elem[parent];
                this.elem[parent] = tmp;
                child = parent;
                parent = (child-1)/2;
            }else{
                break;
            }
        }
    }

    //元素入优先级队列
    //逻辑:将元素放在数组最后位置,然后堆向上调整
    public void push(int val){
        if(isFull()){
            this.elem = Arrays.copyOf(this.elem, this.elem.length*2);
        }
        this.elem[usedSzie] = val;
        this.usedSzie++; //例如元素为10,此时useDsize为11
        adjustUp(usedSzie-1);
    }

    //元素出优先级队列
    //思想:队头和队尾交换 向下调整0下标这棵树
    public int poll(){
        if(isEmpty()) {
            throw new RuntimeException("队列为空");
        }
        int ret = this.elem[0]; //保存队头元素
        int tmp = this.elem[0];
        this.elem[0] = this.elem[usedSzie];
        this.elem[usedSzie] = tmp;
        //相当于原来的队头元素被拿走了
        this.usedSzie--;  /
        adjustDown(0,usedSzie);
        return ret;
    }

    //拿到队头元素不删除
    public int peek(){
        if(isEmpty()) {
            throw new RuntimeException("队列为空");
        }
        return this.elem[0];
    }


    //优先级队列用堆创建,而堆基于数组,当数组已满时,需要扩容
    public boolean isFull(){
        return this.usedSzie == this.elem.length;
    }

    //判空
    public boolean isEmpty(){
        return this.usedSzie == 0;
    }

    public void display(){
        for (int i = 0; i < this.usedSzie; i++) {
            if(i == 0) {
                System.out.print("["+this.elem[i]+" ");
            }else if(i == usedSzie-1){
                System.out.print(this.elem[i]+"]");
            }else{
                System.out.print(this.elem[i]+" ");
            }
        }
    }


}

主函数测试用例

public class TestDemo {
    public static void main(String[] args) {
        MyHeap heap = new MyHeap(10);
        int[] array = {27,15,19,18,28,34,65,49,25,37};
        System.out.println("原来的堆: "+Arrays.toString(array));
        heap.createBigHeap(array);
        System.out.print("现在的大根堆: ");
        heap.display();
        System.out.println();
        heap.push(200);
        heap.display();
        System.out.println(heap.poll());
        heap.display();
    }
}

在这里插入图片描述


【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。