图说PriorityQueue

写offer

offer

  1. 递增当前队列更改次数modCount
  2. 是否超过队列长度,如果是则扩容:如果原长度小于64则每次扩容2,否则每次扩容50%
  3. 递增size
  4. 如果是第一个元素直接放在队列(其实是一个Object数组)0下标处
  5. 添加数据siftUp,如果存在comparator则使用自定义的比较器比较,否则使用元素实现的Comparable接口的比较方法进行比较。暂定使用默认的:siftUpComparable

重建二分最小堆

siftUpComparable

优先级队列底层结构是二分最小堆,按照顺序写入数据:5,1,2,6,8,7,0

  1. 如果父节点(初始值为size)k大于零,继续循环
  2. 获取当前节点(下标=size,不是队列的size,是实际队列中元素数量的size)的父节点1
  3. 当前节点(当前size对应的下标,如果插入第二个节点则,插入前size为1,则当前节点下标=1)大于父节点,直接将放入当前节点2
  4. 当前节点小于父节点,交换当前节点与父节点,并循环继续重复前两步3

案例

按照顺序写入数据:5,1,2,6,8,7,0,插入最后一个元素时的过程
4
x节点=数据0节点,k下标=6
数据0小于父节点数据2,交换父子节点
5
x节点=数据0节点,k下标=2
当前节点下标2大于0继续循环,数据0小于父节点数据1,交换父子节点
6
x节点=数据0节点,k下标=0
当前节点下标=0,结束循环,插入完成。

源码

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;
}

读poll

poll

  1. 如果size为0返回null
  2. 递减size
  3. 递增队列修改次数modCount
  4. 获取队列的第一个元素(下标0处的元素)即最高优先级的数据
  5. 获取最后一个节点x,并将最后一个节点置为null
  6. 如果递减后size不为0则重新构建二分最小堆siftDown,k=0,x=最后一个元素
  7. 返回result即最高优先级的数据

重建二分最小堆

siftDownComparable

  1. 如果第一个元素下标(初始值固定为0)小于size的一半则继续循环
  2. 计算元素的子下标child(左儿子),获取子元素c(左儿子),计算右儿子的下标right(即左儿子下标+1)
  3. 如果右儿子下标小于size(防止越界),并且左儿子大于右儿子,则更新child为右儿子下标,更新c为右儿子。
  4. 如果key小于等于子节点c(左右儿子节点中的最小值),终止循环
  5. 如果步骤3成立,子元素c(即右儿子)写入queue队列的k下标处(初始值始终为0);更新k为元素的子下标child(即右儿子下标)
  6. 否则:子元素c(即左儿子)写入queue队列的k下标处(初始值始终为0);更新k为元素的子下标child(即左儿子下标)
  7. 循环沿发生变动的节点进行比较直至叶子节点。即二分堆遍历
  8. 循环结束将key(即提前取出的队列中的尾巴节点)写入索引k处

案例1:下一个最小值为右子节点,尾巴节点为右子树的叶子节点

拉取一个数据,即优先级最高的root节点,数据为0
7
8
k=0;x=key=最后一个节,数据为2的节点
读取最后一个节点,并将最后一个节点置为null

9
k=0
k小于size的一半3继续循环,右儿子下标2小于size需要比较左右儿子,左儿子大于右儿子,key节点大于左右儿子的最小值,右儿子晋升为父节点

10
k=2
k小于size的一半3继续循环,右儿子下标6等于size不需要比较左右儿子,key节点(即提前取出的最后一个节点,数据为2的节点)小于左右儿子的最小值(data=7),结束循环。将key写入下标k处的节点。

案例2:下一个最小值为左子节点,尾巴节点为右子树的叶子节点

1112
k=0;x=key=最后一个节点,数据为9的节点
读取最后一个节点,并将最后一个节点置为null
13
k=0

k小于size的一半3继续循环,右儿子下标2小于size需要比较左右儿子,左儿子小于右儿子,key节点大于左右儿子的最小值,左儿子晋升为父节点
14
k=1
k小于size的一半3继续循环,右儿子下标4小于size需要比较左右儿子,左儿子大于右儿子,key节点大于左右儿子的最小值,右儿子晋升为父节点
15
k=4
k大于size的一半3结束循环。将key写入下标k处的节点。

案例3:同案例2,只是尾巴节点的数据假设为(即key.data=5.5)

则倒数第二步会有所不同,如下:
16
k=1
k小于size的一半3继续循环,右儿子下标4小于size需要比较左右儿子,左儿子大于右儿子,key节点小于左右儿子的最小值,结束循环。将key写入下标k处的节点。

源码

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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值