PriorityQueue简单介绍


前言

从字面意思来看 ,就能知道,PriorityQueue是和优先级有关系的一种队列

一、PriorityQueue

  1. 内部由一个Object 数组存储数据 transient Object[] queue;
  2. 存储在数组中的数据体现出一个完全二叉堆的结构
  3. 提供peek方法获取数组中的第一个元素(优先级就是这么来的,我们保证数组里面的第一个数据在某种规则下是最优的)

二、完全二叉堆定义

  1. 是一个二叉树结构(所有可以用数组来实现)
  2. 每一个元素的左移子节点都比自身数据大或者小(小顶堆 大顶堆)

三、代码示列

  public static void main(String[] args) {
        Queue queue = new PriorityQueue();
        int[] arr = {39,29,33,50,57,3,78};
        for (int i=0;i<arr.length;i++){
            queue.add(arr[i]);
        }
        System.err.println("优先级最高的元素 :" + queue.peek());

        System.err.println("队列里面的全部数据");
        Iterator iterator = queue.iterator();
        while (iterator.hasNext()){
            System.err.print(iterator.next()+ " ");
        }
        System.err.println("");
        queue.poll();
        System.err.println("移除队列第一个数据后的队列");
        Iterator iterator1 = queue.iterator();
        while (iterator1.hasNext()){
            System.err.print(iterator1.next()+ " ");
        }

    }

在这里插入图片描述

{39,29,33,50,57,3,78} 这个是用来测试的数据,接下来我们用这个数据来分析PriorityQueue的新增,删除流程

四、新增流程

 public boolean offer(E e) {
        if (e == null)
            throw new NullPointerException();
        modCount++;
        int i = size;
        if (i >= queue.length)
            grow(i + 1);
        size = i + 1;
        if (i == 0)
            queue[0] = e;
        else
            siftUp(i, e);
        return true;
    }

这个是添加数据的源码,当添加第一个数据是直接放到数组的第一个位置,当队列里面有数据之后再添加数据就要走siftUp方法了

private void siftUp(int k, E x) {
        if (comparator != null)
            siftUpUsingComparator(k, x);
        else
            siftUpComparable(k, x);
    }

我们这个例子中没有传入comparator,直接看下siftUpComparable方法

private void siftUpComparable(int k, E x) {
        Comparable<? super E> key = (Comparable<? super E>) x;
        while (k > 0) {
            int parent = (k - 1) >>> 1;
            Object e = queue[parent];
            if (key.compareTo((E) e) >= 0)
                break;
            queue[k] = e;
            k = parent;
        }
        queue[k] = key;
    }

这个方法有两个参数k (当前数组的容量,可以理解成当前添加数据可能添加到数组的位置,预占位),x就是当前添加元素的值了
第6行判断条件

if (key.compareTo((E) e) >= 0)

对于入参都是整数来说,如果A小于B A.compareTo(B)返回的是-1,这里也就是key要大于1才满足这个条件

在这里插入图片描述
接下来我们根据这个规则具体来分析下{39,29,33,50,57,3,78}新增的流程,后面描述的数组都是指PriorityQueue内部存储数据的数组

  1. 第一个39数据入队列,直接放到数组的第一个位置

在这里插入图片描述

  1. 第2个数据29入队列(k=1 ,x=29) ,这个时候调用到siftUpComparable方法
  • 算术右移获取父节点位置0
  • 父节点位置的值是39
  • 新添加数据小于父节点的值不满足if条件K号位置设置为父节点的值(也就是数组1号位设置成39)
  • k 设置成父节点的位置(这时候就是0)
  • 不满足k>0的条件退出循环 数组k号位设置当前新增的值

在这里插入图片描述

  1. 第3个数据33入队列(k=2 ,x=33)

    • 算术右移获取父节点位置0
    • 父节点位置的值是29
    • 新添加数据大于父节点的值,满足if条件退出循环
    • 设置数组k(2号位)号位置的值为当前入参33

在这里插入图片描述

  1. 第4个数据50入队列(k=3 ,x=50)

    • 算术右移获取父节点位置1
    • 父节点位置的值是39
    • 新添加数据大于父节点的值 ,满足if条件退出循环
    • 设置数组k(3号位)号位置的值为当前入参50

    在这里插入图片描述

  2. 第5个数据57 入队列(k=4 ,x=57)

    • 算术右移获取父节点位置1
    • 父节点位置的值是39
    • 新添加数据大于父节点的值 ,满足if条件退出循环
    • 设置数组k(4号位)号位置的值为当前入参57

    在这里插入图片描述

  3. 第6个数据3 入队列(k=5 ,x=3)

    • 算术右移获取父节点位置2
    • 父节点位置的值是33
    • 新添加数据小于父节点的值 ,不满足if条件退出循环
    • 父节点的值设置到数组k(这时候是5)号位值
    • k 重新设值(前面拿到的父节点位置2)
    • 继续循环查询k(这时候是2)的父节点位置为0
    • 2号位父节点的值是29,依然不满足if条件,父节点的值(这里是29)设置到数组k(这时候是2)号位值
    • k重新设置成0
    • 退出循环设置数组k(这时候是0)号位置的值为当前入参3
      在这里插入图片描述
  4. 第7个数据78入队列 (k=6 ,x=78)

    • 算术右移获取父节点位置2
    • 父节点位置的值是29
    • 新添加数据大于父节点的值 ,满足if条件退出循环
    • 设置数组k(6号位)号位置的值为当前入参78

在这里插入图片描述
用树来表示就是这样
在这里插入图片描述
正好满足父节点的左右子节点都大于它的规则,这样也就保证了堆最顶端的元素是最小的(可以理解最小就是优先级最高)
跟代码运行结果一致

五 、删除 POLL流程

PriorityQueue提供了这两个方法,这两个都是会删除队列里面的数据,就是有可能让PriorityQueue的二叉堆结构失效

还是上面的例子,poll删除了PriorityQueue内部数组的第一个数据3,就需要有一个最小的数据补上了,不然后面再执行poll操作拿不到数据
另外如果删除了内部数组的第2个数据29 这个位置也要补一个数据上来 ,总体来说 PriorityQueue队列删除跟poll操作后会从队列里面其它位置挪一个数来填充删除元素的位置,这个对应的就是siftDownComparable方法

    private void siftDownComparable(int k, E x) {
        Comparable<? super E> key = (Comparable<? super E>)x;
        int half = size >>> 1;        // loop while a non-leaf
        while (k < half) {
            int child = (k << 1) + 1; // assume left child is least
            Object c = queue[child];
            int right = child + 1;
            if (right < size &&
                ((Comparable<? super E>) c).compareTo((E) queue[right]) > 0)
                c = queue[child = right];
            if (key.compareTo((E) c) <= 0)
                break;
            queue[k] = c;
            k = child;
        }
        queue[k] = key;
    }

这里有两个入参 k代表删除数据在数组中的位置 x代表原来数组中的最后一个元素(在进入这个方法前 先获取到最后一个元素 ,然后释放最后一个数据的空间,本身就删除了一个数据,所以就释放数组最后一个元素的空间 ,然后安装规则判断 原来最后一个元素需要补位到哪里)

在这里插入图片描述
这里面的流程是这样的,我么就已poll流程为例
这时候siftDownComparable方法的入参是0 78

左右节点位置分别是1 跟 2 ,按照if里面的规则右节点的值更小,所以那右节点的值跟最后一个数据比较,看谁替换到已经删除的0号位,这里显然是2号位的子节点29

这个时候K设置成2,再次获取左右子节点 是 5 跟 6 ,因为6等于减1后的数组长度,所以这里直接拿5号位的元素跟 原来最后一个元素比较,还是5号位的子节点数据小点 这时K号位的值重新设置成33

K设置成5 不满足小于层数的条件退出循环,数组5号为重新设值78(原来最后一个元素)

在这里插入图片描述

删除后的队列的树状图

在这里插入图片描述

跟代码运行结果一致

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值