优先级队列(堆)

 日升时奋斗,日落时自省

目录

一、优先级队列

1、概念

2、优先级队列得模拟实现

2.1堆的概念图解

 2.2堆的存储方式

 3、堆的创建

3.1创建一个大根堆 (创建)

3.2在大根堆中插入(插入)

3.3堆中删除(删除)

3.4查看堆顶(探顶) 

一、优先级队列

1、概念

队列是一种先进先出(FIFO)的数据结构,操作的数据可能带有优先级,一般出队列时,可能需要优先级高的元素先出队列
数据结构应该提供两个最基本的操作,一个是返回最高优先级对象,一个是添加新的对象,这就是优先级队列

2、优先级队列得模拟实现

在priorityQueue底层使用了堆的数据结构

2.1堆的概念图解

堆就是将完全二叉树装入一个一维数组中,但是对于节点大小是有要求的,在该完全二叉树中每个根节点大于子节点的就是大根堆,另一种,每个根节点都小于子节点的就是小根堆

堆的性质:

(1)总是一个完全二叉树

(2)堆中某个节点的值总是不大于或不小于其父节点的值

图解:小根堆

 图解:大根堆

 2.2堆的存储方式

将树从根节点按层序进入数组(图解)

注意:堆一定是完全二叉树,因此这里采用是层序的规则采用顺序的方式来高效存储,而如果是非完全二叉树,则不适合使用顺序方式进行存储,为了能够还原二叉树,空间中就会存储空节点,导致空间利用率降低

这里只是图解一个堆中是怎样存储数据的,在实现代码之前我们还需要知道以下几个点:

这里首先设i为节点在数组中的下标:

如果i为0表示根节点,则i节点的双亲节点为(i-1)/2

如果2 * i + 1 小于节点个数,则节点i的左孩子下标为2 * i + 1,否则没有左孩子
如果2 * i + 2 小于节点个数,则节点i的右孩子下标为2 * i + 2,否则没有右孩子

 3、堆的创建

3.1创建一个大根堆 (创建)

小根堆与其相反,以下代码可以稍作调整便可以实现

注释:c表示(child)左孩子节点,p表示(parents)父节点

 如何将一个层序规则完全二叉树的数组改为一个大根堆,这里需要知道最后一个节点当前的父节点满足大根堆,依次倒回去 (图解)

 

当前可以建立一个数组,为我们初期学习的使用 

 实现大根堆每次需要的交换

public void creatHeap(){
        for (int parents = (this.usedSize-1-1)/2; parents >=0 ; parents--) {  //当前就是每次父节点向前移动
            shiftDown(parents,this.usedSize);   //向下交换
        }
    }
    public void shiftDown(int parents,int len){
        int child=2*parents+1;
        while(child<len){           //防止数组越界  孩子节点也是要在有效范围内的
            if(child+1<len&&this.elem[child]<this.elem[child+1]){   //避免没有右孩子,同时也让左孩子永远都是最大的
                child++;
            }
            if(this.elem[child]>this.elem[parents]){
                int tmp=this.elem[child];     
                this.elem[child]=this.elem[parents];
                this.elem[parents]=tmp;         //这里是为了把交大的节点给父节点
                parents=child;            //向下,为了保证当前树下的都是大根堆
                child=parents*2+1;             //孩子节点也向下
            }else {
                break;                   //如果该节点已经是大根堆了就结束当前循环
            }
        }
    }

 运行结果:

3.2在大根堆中插入(插入)

插入:插入值首先放入数组的最后一个位置

 插入条件:(针对写代码)

(1)要判断是不是满了

(2)跳出条件是c(child)==0时就算走完了,所以跳出条件c>0

public void  offer(int val){
        if(isFull()){
            this.elem= Arrays.copyOf(this.elem,2*this.elem.length);
        }
        this.elem[usedSize]=val;
        usedSize++;
        shiftUp(this.usedSize-1);   //因为前面加一,所以这里我们要下标为10的数据就需要-1
    }
    public  void shiftUp(int child){
        int parents=(child-1)/2;
        while (child>0){
            if(this.elem[child]>this.elem[parents]){   //大于的情况下
                int tmp=this.elem[child];              //向上交换
                this.elem[child]=this.elem[parents];
                this.elem[parents]=tmp;
                child=parents;   //向上走
                parents=(child-1)/2;
            }else{
                break;
            }
        }
    }
    public boolean isFull(){   //判断满了没有
        return this.usedSize==elem.length;
    }

3.3堆中删除(删除)

删除堆定元素:直接删除会导致这个树大幅度的移动

思路:

(1)将堆顶与最后一个节点换位置,实现一条路被受影响

(2)从上向下只需要根节点跑一次树的高度就可以

public int poll(){
        if(isEmpty()){   //为空可以抛出一个异常
            throw new NullPointerException();
        }
        int ret=this.elem[0];
        this.elem[0]=this.elem[usedSize-1];   
        this.elem[usedSize-1]=ret;     //将堆顶与最后一个数据交换位置
        usedSize--;
        shiftDown(0,this.usedSize);   //调用一次向下交换
        return ret;
    }
    public  boolean isEmpty(){   //判断是否为空
        return usedSize==0;
    }

3.4查看堆顶(探顶) 

public  int peek(){
        if(isEmpty()){   //为空可以抛出一个异常
            throw new NullPointerException();
        }
        return this.elem[0];
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值