优先队列-PriorityQueue简析

本文详细介绍了Java中的PriorityQueue数据结构,包括其内部实现机制——二叉堆,并解释了添加元素、获取最高优先级元素等核心操作的实现过程。此外,还通过示例展示了如何在Java中使用PriorityQueue。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

PriorityQueue是优先队列,能根据优先级,依次取出元素,它是由二叉堆实现的,同时二叉堆也是实现优先队列的经典数据结构,它是一颗完全的二叉树,所有的叶子节点都在同一层,有2种实现,最大堆,每个节点都要大于它的孩子节点,最小堆反之。下面我们用图,来演示下自底向上构造堆,ACEJHX顺序插入。
这里写图片描述
由图可以看出,每次插入都是把元素放到树的最后,然后沿着父节点依次调整,直到不大于它的父节点为止。由于每次调整的都不超过树的高度,故时间复杂度为logn。
而取出操作也是相似的调整,只需把最后一个元素放到根处,然后依次与子节点比较,如果子节点大于自己,则进行交换,直到子节点全部小于自己位置,时间复杂度同样不超过树的高度logn。而堆排序就是n个元素组成堆,然后依次取出的过程,时间复杂度为nlogn。

现在基本的原理已经清楚了,我们看看java中如何实现堆的。看下构造函数:

    public PriorityQueue(int initialCapacity,
                         Comparator<? super E> comparator) {
        this.queue = new Object[initialCapacity];
        this.comparator = comparator;
    }

发现java中是有数组来实现堆的(默认是最小堆),因为堆是完全二叉树,我们可以很容易用用数组来实现,只要定义:节点位置为i,那么左孩子位置在2i +1处,右孩子在2i+2处,而孩子节点的父节点则为(k-1)/2,我们来看看添加操作:

public boolean offer(E e) {
        int i = size;//数组从0开始,size处就是要插入的新元素
        if (i >= queue.length)
            grow(i + 1);
        size = i + 1;
        if (i == 0)
            queue[0] = e;
        else
            siftUp(i, e);
        return true;
    }

 private void siftUp(int k, E x) {
        if (comparator != null)
            siftUpUsingComparator(k, x);
        else
            siftUpComparable(k, 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 e = queue[parent];
            if (key.compareTo((E) e) >= 0)//如果大于父节点,那么肯定也大于父节点之上的父节点
                break;
            queue[k] = e;
            k = parent;
        }
        queue[k] = key;
    }

接下来,我们看看获取最高优先级的元素操作,也是移除操作:

 public E poll() {
        if (size == 0)
            return null;
        int s = --size;
        modCount++;
        E result = (E) queue[0]; //优先级第0个拥有永远的最高价
        //以下是重新调整堆的结构
        E x = (E) queue[s];
        queue[s] = null;
        if (s != 0)
            siftDown(0, x);
        return result;
    }

     private void siftDown(int k, E x) {
        if (comparator != null)
            siftDownUsingComparator(k, x);
        else
            siftDownComparable(k, x);
    }
    //最小堆的调整,从跟节点开始,孩子节点最小元素和最后节点比较,小于最后节点,则上移,最小孩子节点递归比较,直到最小孩子节点不小于最后节点为止
      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;
    }

需要注意的是:对于优先集合遍历的操作是按照数组先后顺序提取元素的,并非优先级。
最后演示下使用:

public class SimpleJava{

    public static void main(String[] args) {
        PriorityQueue<Integer> q = new PriorityQueue<Integer>();
        q.add(1);
        q.add(3);
        q.add(4);
        q.add(2);
        int length = q.size();
        for(int i = 0; i < length; i++)
            System.out.println(q.poll());

    }
}

打印:

1
2
3
4
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值