java 优先队列源码阅读

Java PriorityQueue阅读

优先队列使用最大或者最小堆来实现,使用数组来储存元素,将数组当做完全二叉树来处理,节点node的左右孩子节点为(2node+1和2node+2)
数组扩容:newCapacity =
oldCapacity+((oldCapacity<64) ?(oldCapacity+2) : (oldCapacity>>1));当newCapacity>Integer.MAX_VALUE - 8时,由private static int hugeCapacity(int minCapacity)处理
最后面的代码是我自己的仿写,有不确定的地方请参考源码

PriorityQueue参数介绍

参数名作用
Object[] queue保存输入的元素
size已经存入的元素的数量
DEFAULT_INITIAL_CAPACITY默认的队列初始化大小
Comparator<? super E> comparator比较存储的元素的顺序
int modCount队列修改的次数

PriorityQueue的构造方法

构造方法分为两类:

  1. 根据参数进行构造,如:public PriorityQueue(int initialCapacity, Comparator<? super E> comparator)
  2. 将Collection作为参数进行构造,如:public PriorityQueue(Collection<? extends E> c);其中的Collection可以为SortedSet,PriorityQueue或者其他实现自Collection的类

PriorityQueue的public方法

方法名作用和使用
public boolean add(E e)向队列中添加元素,调用offer方法实现
public boolean offer(E e)向队列中添加元素,e==null抛出异常,否则返回true
public E peek()返回优先队列中的第一个元素,及queue[0]
public boolean remove(Object o)从队列中删除o元素,成功返回true,否则返回false
public boolean contains(Object o)队列中是否存在元素o
public Object[] toArray()返回队列中的元素(数组queue)的拷贝
public T[] toArray(T[] a)将queue数组中的元素转换为T类型并拷贝到T[] a中,返回a
public Iterator iterator()返回一个队列的Iterator对象
public void clear()清空数组queue
public E poll()删除并返回优先队列中的第一个元素(queue[0])

PriorityQueue的private方法

我在这里根据作用把他们分为三类:

  1. 增加队列的容积:
    private void grow(int minCapacity):在offer方法中使用,minCapactiy = queue.length+1;增加方法为:newCapacity =
    oldCapacity+((oldCapacity<64) ?(oldCapacity+2) : (oldCapacity>>1));

  2. 平衡队列中的数据顺序
    private void siftUp(int k,E x):k:当前元素的插入位置,x插入的元素,在增加元素和删除元素时被使用,但是这个方法不负责具体实现,只是用来判断队列中是否申明了Comparator,申明了调用私有方法siftUpUsingComparator方法去具体实现,否则调用siftUpComparable方法;下面会具体说明源代码的实现
    private void siftUpComparable(int k, E x):k和x同上,负责向上平衡queue(平衡方法可参考最小堆的平衡方法)平衡时的比较使用E的CompareTo方法
    private void siftUpUsingComparator(int k, E x):k和x同上,负责向上平衡queue(平衡方法可参考最小堆的平衡方法)平衡时的比较使用Comparator的规则
    还有向下平衡的三个私有方法:基本内容同上面三个方法
    private void siftDown(int k, E x):
    private void siftDownComparable(int k, E x):
    private void siftDownUsingComparator(int k,E x):

  3. 查和删除的辅助方法
    private int indexOf(Object o): 查找对象o在队列中的位置,在public类remove中被调用;
    private E removeAt(int i): 删除队列queue中位于第i位置的元素并平衡queue,在public类remove和私有类Iterator中的remove中被调用,下面会详细讲解这个方法;

主要源码讲解

  1. private void siftUpComparable(int k, E x)和private void siftUpUsingComparator(int k, E x)方法的实现:
/**
 * 在queue中的第k个位置(从位置0开始)放入x,并向上调整顺序
 *  与父节点比较,如果比父节点顺序靠前,与父节点位置交换 
 * @param k 开始时放入元素x的位置
 * @param x 放入的元素
 */private void siftUpComparable(int k, E x) {
    Comparable<? super E> key=(Comparable<? super E>)x;
    while (k>0){
        int parent=(k-1)>>>1;
        Object pNode=queue[parent];
        if(key.compareTo((E)pNode)>0)
            break;
        queue[k]=pNode;
        k=parent;
    }
    queue[k]=key;
}
  1. private void siftDownComparable(int k, E x)和private void siftDownUsingComparator(int k,E x)方法的实现:
/**
 * 在queue中的第loca个位置(从位置0开始)放入x,并向下调整顺序
 * 先找出子节点中顺序靠前的节点,再与父节点的顺序进行比较,如果顺序靠前的子节点的顺序在父节点的前面,父节点与子节点交换位置
 * @param loca 开始时放入元素x的位置
 * @param x 放入的元素
 */
pri
private void siftDownComparable(int loca,E x){
    Comparable<? super E> key=(Comparable<? super E>)x;
    int half=size>>>1;
    // size为保存元素的数组的大小,第n位的左右子节点为(2*n+1)和(2*n+2),所以当loca小于size/2时,loca不存在子节点
    while (loca<half){
        int child=(loca<<1)+1;
        Object childNode=queue[child];
        int right=child+1;
        //siftDownUsingComparator方法中的比较方法为:
        //comparator.compare((E)childNode,(E)queue[right])>0
        if(right<size
                &&((Comparable<? super E>)childNode).compareTo((E)queue[right])>0){
            childNode=queue[right];
            child=right;
        }
        if(key.compareTo((E)childNode)<=0)
            break;
        queue[loca]=childNode;
        loca=child;
    }
    queue[loca]=x;
}

  1. 内部类Iterator的实现
//这是一个很重要的函数,优先队列中的所有remove方法都掉用了这个方法来完成
/**
*作用删除在queue数组中位于第index的元素
*实现,使用数组末尾的元素来替换第index位的元素,替换后会先调用siftDown(向下平衡)方法,
*如果该元素没有向下移动(判断标准为位置是否变化),再使用siftUp方法向上平衡。
*该元素的最终位置小于index,返回该元素,否则返回null。返回值主要是在Iteretor的remove方法中用到,为了防止删除元素后,新填充的元素顺序比删除的元素顺序靠前而没有遍历。
*/
E removeAt(int index) {
    int s=--size;
    if(s==index){
        queue[index]=null;
    }else{
        E moved=(E)queue[s];
        siftDown(index,moved);
        //没有向下移动的时候,queue[index]与moved的引用相同
        if(queue[index]==moved){
            siftUp(index,moved);
            if (queue[index]!=moved)
                return moved;
        }
    }
    return null;
}
//这是迭代器的实现,这里面的next方法和remove方法是优先队列中最难理解的部分,
//remove使用前next方法必须被使用
private final class Itr implements Iterator<E>{
    //当前遍历在queue中的位置
    private int cursor;
    //标记当前遍历的元素是在数组中还是在forgetMeNot,在forgetMeNot中lastRet=-1;
    private int lastRet=-1;
    //保存removeAt的返回值
    private ArrayDeque<E> forgetMeNot;
    //当前遍历的元素
    private E lastRetElt;
    Itr(){}

    @Override
    public boolean hasNext() {
        return cursor<size||
                (forgetMeNot!=null&&!forgetMeNot.isEmpty());
    }
    //先遍历queue中的在遍历forgetMeNot中的
    @Override
    public E next() {
        if(cursor<size)
            return (E) queue[lastRet=cursor++];
        if(forgetMeNot!=null){
            lastRet=-1;
            lastRetElt=forgetMeNot.poll();
            if(lastRetElt!=null)
                return lastRetElt;
        }
        throw new NoSuchElementException();
    }
    //removeAt返回!=null时,说明末尾的元素比删除的元素的顺序更靠前(前面有说),继续遍历
    //queue不能遍历到这个元素,所以把它加入forgetMeNot中,返回null时,说明已经遍历过的元素
    //位置没有变化,并且用没有遍历过的元素替代了删除的元素,所以cursor--;
    public void remove(){
        if(lastRet!=-1){
            E moved=PriorityQueueRewrite.this.removeAt(lastRet);
            lastRet=-1;
            if(moved==null){
                cursor--;
            }else{
                if(forgetMeNot==null){
                    forgetMeNot=new ArrayDeque<>();
                }
                forgetMeNot.add(moved);
            }
        }else if(lastRetElt!=null){
            PriorityQueueRewrite.this.removeEq(lastRetElt);
            lastRetElt=null;
        }else throw new IllegalStateException();
    }
}
  1. queue的接口的实现:
    添加:在数组末尾添加,然后向上平衡
    poll:返回数组头部元素,将数组末尾的元素移到首部,然后向下平衡
    实现详情请参考最大堆的的实现
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值