优先级队列(堆)

发布时间:2024年01月23日

目录

1 概念

2?堆的概念

2.1小根堆

2.2大根堆?

3堆的存储方式???????

4、堆的创建?

4.1堆向下调整

5、时间复杂度

6、堆的插入(向上调整)

7、堆的删除

8、PriorityQueue的特性?

9、堆排序


1 概念

???????我们知道的队列,队列是一种先进先出(FIFO)的数据结构但有些情况下,操作的数据可能带有优先级,一般出队 列时,可能需要优先级高的元素先出队列,该中场景下,使用队列显然不合适,比如:在手机上玩游戏的时候,如果有来电,那么系统应该优先处理打进来的电话;初中那会班主任排座位时可能会让成绩好的同学先挑座位。
JDK1.8 中的 PriorityQueue 底层使用了堆这种数据结构,而堆实际就是在 完全二叉树 的基础上进行了一些调整。

2?堆的概念

2.1小根堆

2.2大根堆?

3堆的存储方式

从堆的概念可知, 堆是一棵完全二叉树,因此可以层序的规则采用顺序的方式来高效存储

4、堆的创建?

4.1堆向下调整

对于集合 { 27,15,19,18,28,34,65,49,25,37 } 中的数据,如果将其创建成堆呢?
向下过程 ( 以小堆为例 )
1. parent 标记需要调整的节点, child 标记 parent 的左孩子 ( 注意: parent 如果有孩子一定先是有左孩子 )
2. 如果 parent 的左孩子存在,即 :child < size , 进行以下操作,直到 parent 的左孩子不存在
parent 右孩子是否存在,存在找到左右孩子中最小的孩子,让 child 进行标
parent 与较小的孩子 child 比较,如果: parent小于较小的孩子 child ,调整结束 否则:交换parent 与较小的孩子 child ,交换完成之后, parent 中大的元素向下移动,可能导致子树不满足对的性质,因需要继续向下调整,即parent = child child = parent*2+1; 然后继续步骤 2
//创建堆
public void creatHeap(){
        int preheap;//每次的根节点
        for ( preheap=(usesize-1-1)/2;preheap>=0;preheap--){
            signHeap(preheap,usesize);
        }

    }
    //向下调整为大根堆为例
    private void signHeap(int preheap,int end){//向下调整
        int child=preheap*2+1;//preheap左子树下标
        while (child<end){
            if(child+1<end&&elem[child]<elem[child+1])child++;
            //现在child指向孩子节点的最大值
            if (elem[preheap]<elem[child]){
                swap(preheap,child);
               preheap=child;
                child=child*2+1;
            }
            else break;
        }
    }
    private void swap(int i,int j) {
        int tmp = elem[i];
        elem[i] = elem[j];
        elem[j] = tmp;
    }

5、时间复杂度

因为堆是完全二叉树,而满二叉树也是完全二叉树,此处为了简化使用满二叉树来证明 ( 时间复杂度本来看的就是近似值,多几个节点不影响最终结果)

6、堆的插入(向上调整)

堆的插入总共需要两个步骤:
1. 先将元素放入到底层空间中(注意:空间不够时需要扩容)
2. 将最后新插入的节点向上调整,直到满足堆的性质

   public void offer(int key){
        if(isFull()) Arrays.copyOf(elem,elem.length*2);//扩容
        elem[usesize]=key;
        usesize++;
        //调整为大根堆,向上调整
        siftUp(usesize-1);
    }
    //向上调整
    private void siftUp(int child) {
        int parent = (child-1)/2;
        while (child > 0) {//或者parent>0
            if(elem[child] > elem[parent]) {
                swap(child,parent);
                child = parent;
                parent = (child-1)/2;
            }else {
                break;
            }
        }
    }

7、堆的删除

1. 将堆顶元素对堆中最后一个元素交换
2. 将堆中有效数据个数减少一个
3. 对堆顶元素进行向下调整

public int poll() {
        int tmp = elem[0];
        swap(0,usesize-1);
        usesize--;
       signHeap(0,usesize);//0下标元素和最后一个元素换,之后只有0下标还不是大根堆,调整
        return tmp;
    }

8、PriorityQueue的特性?

1. 使用时必须导入PriorityQueue所在的包,即:import java.util.PriorityQueue;
2. PriorityQueue中放置的元素必须要能够比较大小,不能插入无法比较大小的对象,否则会抛出
ClassCastException异常(放入自定义元素类可以让该类继承Comparable并重写ComparTo方法,或者写比较器)例:

class Imp implements Comparator<Integer>{

    @Override
    public int compare(Integer o1, Integer o2) {
        return o2.compareTo(o1);//大根堆
    }
}
public class Test1 {
    public int[] smallestK(int[] arr, int k) {
        //堆的默认比较时小根堆,我们建立大根堆需要比较器
        PriorityQueue<Integer> p=new PriorityQueue<Integer>(new Imp());


3. 不能插入null对象,否则会抛出NullPointerException
4. 没有容量限制,可以插入任意多个元素,其内部可以自动扩容
5. 插入和删除元素的时间复杂度为
6. PriorityQueue底层使用了堆数据结构
7. PriorityQueue默认情况下是小堆---即每次获取到的元素都是最小的元素

9、堆排序

见下一篇博客,介绍所有排序

文章来源:https://blog.csdn.net/weixin_54783024/article/details/135740141
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。