【java集合】PriorityQueue集合及源码实现

PriorityQueue (优先级队列)

1、集合特点

PriorityQueue 优先级队列 是queue接口下的集合 存储单值
由实现的接口 — 》 特点:
1)Collection接口:集合顶级接口
是存放一组单值的最大接口。 所谓的单值是指集合中的每个元素都是一个对象。
一般很少直接使用此接口直接操作。

2)Queue接口:具有队列的性质
队列接口,具有队列先入先出的特点。此接口的子类可以实现队列操作 优先级队列有队列的性质。

传统先入先出:先添加的元素先弹出 == 先添加元素优先级最高。把优先级高的先弹出
传统队列的优先级比较原则:先添加的元素先弹出

优先级队列的先入先出:我们需要提供一个优先级比较原则,优先级队列根据这个原则。
判断谁是最小的,谁最小谁的优先级高。谁的优先级高谁先被弹出。

3)Serializable :这个集合是可以被序列化的

4)Iterator : 这个集合是可以使用迭代器进行从前往后的迭代。

2、集合使用

底层是数组
维护小根堆结构:父节点小于左节点和右节点,而左右节点的大小不能确定
父节点:i
左孩子:2i+1
右孩子:2i+2

优先级队列进行使用(主要是提供比较原则)

如何提供优先级队列的比较原则?

和TreeMap中 提供大小比较原则相同
相当于我们也需要给优先级队列提供一个大小比较的原则 哪个比较小哪个优先级就高
优先级队列我们也是通过提供比较器的方式 提供的大小比较的方式的。(外比较器,内比较器)

3、基本方法及源码实现

1)构造方法及全局变量
(可以使用外比较器的构造函数:如果未提供外比较器则设为null)

public class MyPriorityQueue<E> {

    private Object []queue;
    private final static int DEFAULT_CAPACITY=11;//数组的初始容量
    private final Comparator<E> comparator ;//继承的外比较器
    private int size;//元素的个数

    public MyPriorityQueue() {
        queue =new Object[DEFAULT_CAPACITY ];
        this .comparator =null ;
    }

    public MyPriorityQueue (Comparator<E> comparator ){//有外部比较器的构造函数
        queue =new Object[DEFAULT_CAPACITY ];
        this .comparator =comparator ;

    }

2)boolean remove(Object o) o代表所要删除的元素 删除一个指定元素 如果元素不存在返回false,如果删除成功返回true
a)先找到待删除元素的下标
b)如果删除的是最后一个元素,直接删除
c)如果不是最后一个元素,将待删元素置空,最后一个元素放在删除元素下标的位置
d)为了维护小根堆结构,要进行比较:先向下调整,因为父节点小于子节点,所以最后一个节点大于待删节点的父节点的概率比较大,所以先和下面的节点进行比较,如果未进行向下调整则需要进行向上调整,因为小根堆不能保证左右节点的大小关系,如果左大于右,则可能进行向上调整,进行了向下调整则不需要进行向上调整

  /**
     * 删除操作
     * @param o
     * @return
     */
    public boolean remove(Object o){
        int i=indexOf(o);//找到o元素所在的下标

        if(i==-1){
            return false ;
        }else {
            //开始进行删除
            int s=--size ;//获取最后一个元素的下标值 并将个数-1
            if(i==s){//如果删除的是最后一个元素,不需要调整,直接删除
                queue [i]=null ;
            }
            //将最后一个元素放在待删除下标下进行对比
            E movd= (E) queue [s];
            queue [i]=null ;
            siftDown(i,movd );//先向下调整,由于底层是小根堆结构,所以向下调整的概率比较大
            if(queue [i]==movd ){//若未进行向下调整,则需向上调整
                siftUp(i,movd );
            }
        }return true ;
    }


    private int indexOf(Object o) {
        if(o==null ){
            return -1;
        }
        for(int i=0;i<size ;i++){
            if(o.equals(queue [i]) ){
                return i;
            }
        }
        return -1;
    }
     /**
     * 向下调整操作
     * i:调整元素的下标
     * x:待调整元素
     * @param i
     * @param x
     */
    private void siftDown(int i, E x) {//i 是x的下标
        //循环退出的条件:父节点的值比本身小
        //大小比较原则,通过外比较器或者内比较器 外比较器的优先级高于内比较器
        if(comparator !=null ){//有外比较器,进行外比较器调整
            siftDownByComparator(i,x);
        }else {
            siftDownByComparable(i,x);
        }
        
    }

    /**
     * 内比较器的向下调整
     * @param i
     * @param x
     */
    private void siftDownByComparable(int i, E x) {
        Comparable  <? super E> key=(Comparable  <? super  E> )x;
        //int left=2*i+1;
        //int right=left+1;
        int half=size>>>1;//计算出没有孩子节点的最小下标

        while (i<half ){
            int child=i<<1+1;
            Object c=queue [child ];
            //c是左孩子的值
            //child是左孩子的下标
            int right=child +1;//right是右孩子的坐标,此时并不能保证一定有右孩子
            if(right <size ) {
                Object r = queue[right];
                Comparable<? super E> ckey = (Comparable<? super E>) c;
                if (ckey.compareTo((E) r) > 0) {
                    child = right;
                    c = r;
                    //c是右孩子的值
                    //child是右孩子的下标
                }
            }
            if(key .compareTo((E)c)<0 ){
                break;
            }
            queue [i]=c;//将孩子的值与赋给父节点
            i=child ; //更新父节点
        }
        queue [i]=x;//将父节点的值赋给孩子
    }

    /**
     * 外比较器的向下调整
     * @param i
     * @param x
     */
    private void siftDownByComparator(int i, E x) {
        //int left=2*i+1;
        //int right=left+1;
        int half=size>>>1;//计算出没有孩子节点的最小下标

        while (i<half ){
            int child=i<<1+1;
            Object c=queue [child ];
            //c是左孩子的值
            //child是左孩子的下标
            int right=child +1;//right是右孩子的坐标,此时并不能保证一定有右孩子
            if(right <size ) {
                Object r = queue[right];
                if (comparator .compare((E) c,(E)r)>0 ) {
                    child = right;
                    c = r;
                    //c是右孩子的值
                    //child是右孩子的下标
                }
            }
            if(comparator .compare(x,(E)c)<0 ){
                break;
            }
            queue [i]=c;//将孩子的值与赋给父节点
            i=child ; //更新父节点
        }
        queue [i]=x;//将父节点的值赋给孩子
    }


3)boolean offer(E e) { 添加一个指定元素 添加元素不能为空,否则返回空指针异常

a)先判断是否需要扩容if(i>=queue .length )
b)如果需要扩容则进入到扩容方法,当size小于64时,进行2倍加2扩容,当大于64时,进行1/2扩容,而新数组的容量也不能大于Integer .MAX_VALUE -8
c)然后进行添加操作,如果是第一个添加则放到0号下标下,其他则进行向上调整(如果当前比父节点小,则交换位置维护小根堆结构)
小根堆结构:父节点小于左节点和右节点,而左右节点的大小不能确定
d)因为比较器的优先级:外比较器》内比较器,所以调整方法又分为外比较器的向上调整和内比较器的向上调整 外比较器主要是comparator .compare(e , x)>= 0 内比较器主要是Comparable <? super E> key=(Comparable <? super E> )e;将比较器的对象取出,然后key .compareTo(x)>=0

 /**
     * 队列增加操作
     * @param e
     * @return
     */
    public boolean  offer(E e){
        if(e==null ){
            throw new NullPointerException();
        }
        int i=size ;
        if(i>=queue .length ){
            grow(size +1);
        }
        size ++;

        if(i==0){
            queue [0]=e;
        }else {
            //先将元素放到最后一个位置上,再向上进行调整
            //如果新插入的元素比父节点小,则与父节点进行交换
            //反之,不做调整
            siftUp(i,e);

        }
        return true ;
    }


    private static int MAX_ARRAY_SIZE=Integer .MAX_VALUE -8;//容量边界

    /**
     * 扩容操作
     * @param mincapacity
     */
    private void grow(int mincapacity) {//当前容量小于64时,2倍加2扩容,大于64时,1/2扩容

        int oldcapacity=queue .length ;
//        if(size <64){
//            newcapacity = 2*oldcapacity  +2;
//        }else {
//            newcapacity =oldcapacity  >>1+oldcapacity  ;
//        }
        int newcapacity =oldcapacity +((oldcapacity < 64)? (oldcapacity +2): (oldcapacity >>1));//三位运算符
        if(newcapacity - MAX_ARRAY_SIZE>0){//如果newcapacity大于MAX_ARRAY_SIZE,为了对新数组容量边界控制,进行调整
            newcapacity =hugeCapacity(mincapacity );
        }
        queue =Arrays .copyOf(queue ,newcapacity );
    }

    private int hugeCapacity(int mincapacity) {//控制newcapacity的容量
        if(mincapacity <0){//
            throw new OutOfMemoryError() ;
        }
        return (mincapacity > MAX_ARRAY_SIZE ) ? Integer .MAX_VALUE : MAX_ARRAY_SIZE ;//如果 mincapacity > MAX_ARRAY_SIZE,则使newcapacity=Integer .MAX_VALUE
        //如果 mincapacity < MAX_ARRAY_SIZE,则使newcapacity=MAX_ARRAY_SIZE
    }

    private void siftUp(int i, E e) {//i 计算父节点的位置
        //循环退出的条件:父节点的值比本身小
       //大小比较原则,通过外比较器或者内比较器 外比较器的优先级高于内比较器
        if(comparator !=null ){//有外比较器,进行外比较器调整
            siftUpByComparator(i,e);
        }else {
            siftUpByComparable(i,e);
        }
    }

    /**
     * 外比较器的向上调整
     * @param i
     * @param e
     */
    private void siftUpByComparator(int i, E e) {
        while (i>0){
            int par=(i-1)>>1;
            E x=(E)queue [par ];
            if(comparator .compare(e , x)>= 0 ){//当前节点不小于父节点
                break;
            }
            queue [i]=x;
            i=par ;
        }
        queue [i]=e;
    }

    /**
     * 内比较器的向上调整
     * @param i
     * @param e
     */
    private void siftUpByComparable(int i, E e) {
        Comparable  <? super E> key=(Comparable  <? super  E> )e;
        while (i>0){
            int par=(i-1)>>>1;//无符号右移
            E x=(E)queue [par ];
            if(key .compareTo(x)>=0 ){//当前节点不小于父节点
                break;
            }
            queue [i]=x;
            i=par ;
        }
        queue [i]=e;
    }

4)boolean add(E e) { 添加一个指定元素
return offer(e);
}

  public boolean add(E e){
        return offer(e);
    }

    /**
     * 返回优先级最小元素 并不删除, 如果没有元素返回null
     * @return
     */
    public E peek(){
        if(size ==0 ){
            return null ;
        }
        return (E) queue [0];
    }

5)E peek() 取出队头元素但不删除 如果当前队头没有元素返回null

队头元素:优先级最高那个元素 小根堆的堆顶元素 数组0号下标
队头没有元素: 优先级队列中没有元素 返回null


    /**
     * 返回优先级最小元素 并不删除, 如果没有元素返回null
     * @return
     */
    public E peek(){
        if(size ==0 ){
            return null ;
        }
        return (E) queue [0];
    }

6)E element() 取出队头元素但不删除 如果当前队头没有元素抛出异常
减少重复代码的出现

 /**
     * 返回优先级最小元素 并不删除,如果没有元素返回异常
     * @return
     */
    public E element(){
//        if(size ==0){
//            throw new NullPointerException() ;
//        }
//        return (E) queue [0];
        E x=peek() ;
        if(x==null ){
            throw new NullPointerException() ;
        }else {
            return x;
        }
    }

7)E poll() 取出队头元素并删除 如果当前队头没有元素返回null

 /**
     * 返回优先级最小的元素 并删除 如果没有元素返回null
     * @return
     */
    public E poll(){
        if(size ==0 ){
            return null ;
        }
        E res= (E) queue [0];

        //删除队头元素
        int s=--size ;
        E x= (E) queue [s];
        queue [s]=null ;
        if(s!=0){//进行向下调整  s==0时说明只有一个元素,不需要调整
            siftDown(0,x);//i 是x的下标
        }
        return res;

    }

8)迭代器和ArrayList的差不多

 /**
     * 迭代器的实现
     * @return
     */
    public Iterator<E> iterator() {
        return new Itr();
    }
    private final class Itr implements Iterator<E> {
        private int cur=0;//当前元素的下标

        @Override
        public boolean hasNext() {
            return cur < size ;
        }

        @Override
        public E next() {
            int i=cur ;
            if(i>=size ){
                throw new NoSuchElementException() ;
            }
            cur =cur +1;
            return (E)queue [i ];
        }

        @Override
        public void remove() {
            try {
                MyPriorityQueue .this .remove(queue [cur ]);
            }catch(IndexOutOfBoundsException  e) {
                throw new ConcurrentModificationException() ;
            }
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值