PriorityQueue优先队列实现原理

转载: http://blog.csdn.net/lisuyibmd/article/details/53205403
一、什么是优先队列

优先队列不是按照普通对象先进先出原FIFO则进行数据操作,其中的元素有优先级属性,优先级高的元素先出队。本文提到的PriorityQueue队列,是基于最小堆原理实现。

需要注意:PriorityQueue继承了AbstractQueue没有实现BlockingQueue接口,所以没有take阻塞方法。

二、什么是最小堆

最小堆是一个完全二叉树,所谓的完全二叉树是一种没有空节点的二叉树。

最小堆的完全二叉树有一个特性是根节点必定是最小节点,子女节点一定大于其父节点。还有一个特性是叶子节点数量=全部非叶子节点数量+1

在 PriorityQueue队列中,基于数组保存了完全二叉树。所以在已知任意一个节点在数组中的位置,就可以通过一个公式推算出其左子树和右子树的下标。已知节点下标是i,那么他的左子树是2*i+1,右子树是2*i+2。

三、PriorityQueue队列的存取原理

 1、首先看下源码

[java] view plain copy 在CODE上查看代码片派生到我的代码片

/** 
 * Inserts item x at position k, maintaining heap invariant by 
 * promoting x up the tree until it is greater than or equal to 
 * its parent, or is the root. 
 * 
 * To simplify and speed up coercions and comparisons. the 
 * Comparable and Comparator versions are separated into different 
 * methods that are otherwise identical. (Similarly for siftDown.) 
 * 
 * @param k the position to fill 
 * @param x the item to insert 
 */  
private void siftUp(int k, E x) {  
    if (comparator != null)  
        siftUpUsingComparator(k, x);  
    else  
        siftUpComparable(k, x);  
}  

在调用offer(E e)方法入队时,首先进行非null判断,然后是grow方法扩容,紧接着就是siftUp方法调整节点位置,那么是如何调整的呢?为什么要调整?
2、为什么要调整节点顺序?

因为这是一个最小堆,最小堆必须要严格按照子节点大于父亲节点的顺序做数组中存放。

3、如何调整?

siftup方法有个if-else判断,如果有比较器,则使用siftUpUsingComparator(k, x);如果没有则调用siftUpComparable(k, x);这个方法会默认给一个比较器。

比较什么呢??我们说最小堆实现了这个队列,队列一定有入队操作,入队是元素首先进入队列尾,然后和自己的父节点比较,像冒泡一样将该节点冒到合适的位置,即比自己的父亲节点大,比自己的儿子节点小。

[java] view plain copy 在CODE上查看代码片派生到我的代码片
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;  
   }  
4、如何出队
[java] view plain copy 在CODE上查看代码片派生到我的代码片
public E poll() {  
        if (size == 0)  
            return null;  
        int s = --size;  
        modCount++;  
        E result = (E) queue[0];  
        E x = (E) queue[s];  
        queue[s] = null;  
        if (s != 0)  
            siftDown(0, x);  
        return result;  
    }  

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

出队和入队原理正好相反,每次出队也要进行siftDown操作,对元素顺序重新整理。

四、总结

PriorityQueue队列不适合进场出队入队的频繁操作,但是他的优先级特性非常适合一些对顺序有要求的数据处理场合。

版权声明:沉淀、积累,我追求工匠精神

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值